vimp-engine 0.37.1 → 0.38.1
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.
- package/package.json +1 -1
- package/src/client/boot.js +1 -1
- package/src/client/components/controller/ClientReports.js +2 -1
- package/src/client/components/controller/Games.js +1 -1
- package/src/client/components/controller/LobbyAuth.js +101 -15
- package/src/client/components/controller/Profile.js +95 -0
- package/src/client/components/model/Games.js +1 -1
- package/src/client/components/model/LobbyAuth.js +382 -115
- package/src/client/components/model/Profile.js +144 -0
- package/src/client/components/view/LobbyAuth.js +150 -62
- package/src/client/components/view/Profile.js +372 -0
- package/src/client/lib/authApi.js +106 -0
- package/src/client/lib/hostCaps.js +3 -2
- package/src/client/lib/pkce.js +23 -0
- package/src/client/lib/roomLink.js +1 -1
- package/src/client/lib/sessionSync.js +45 -0
- package/src/client/lib/signalingErrors.js +3 -3
- package/src/client/lib/turnstile.js +130 -0
- package/src/client/main.js +132 -26
- package/src/client/network/TokenRenewer.js +99 -0
- package/src/client/session/GuestSession.js +63 -17
- package/src/client/session/Membership.js +2 -1
- package/src/client/style.css +128 -16
- package/src/client/views/includes/lobby.pug +11 -6
- package/src/client/views/includes/lobbyAuth.pug +34 -16
- package/src/client/views/includes/profile.pug +73 -0
- package/src/client/views/index.pug +1 -0
- package/src/config/authClient.js +55 -30
- package/src/config/chatMessages.js +1 -2
- package/src/config/env.js +2 -2
- package/src/config/lobby.js +45 -1
- package/src/config/master.js +8 -5
- package/src/config/wsports.js +4 -1
- package/src/devtools/builtinScenario.js +1 -1
- package/src/devtools/contract/rules/a2-package-scripts.js +1 -1
- package/src/devtools/contract/rules/a4-vite-config.js +1 -1
- package/src/devtools/contract/rules/b8-system-messages.js +1 -1
- package/src/devtools/invariants.js +1 -1
- package/src/devtools/pluginLoader.js +1 -1
- package/src/host/GameCoreAdapter.js +0 -3
- package/src/host/HostGame.js +28 -0
- package/src/host/PortMachine.js +50 -1
- package/src/host/meta/SocketManager.js +0 -13
- package/src/host/meta/core/RoundManager.js +0 -19
- package/src/host/meta/modules/PlayerDataSync.js +29 -3
- package/src/host/meta/modules/chat/systemMessages.js +1 -4
- package/src/lib/loadGamePackage.js +1 -1
- package/src/lib/validators.js +8 -0
- package/src/standalone/index.js +1 -1
package/package.json
CHANGED
package/src/client/boot.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Режим загрузки клиента движка (Этап 2 плана standalone-sdk). Клиент один
|
|
2
2
|
// на все контуры, а различает их этот модуль:
|
|
3
3
|
//
|
|
4
|
-
// lobby — прод: каталог игр и сигналинг мастера, лобби за
|
|
4
|
+
// lobby — прод: каталог игр и сигналинг мастера, лобби за экраном профиля;
|
|
5
5
|
// solo — standalone SDK: хост в этой же вкладке (inline, без Worker'а),
|
|
6
6
|
// мастера нет вовсе;
|
|
7
7
|
// dedicated — Node-сервер: прямой WebSocket к игровому процессу.
|
|
@@ -25,7 +25,8 @@ export default class ClientReportsCtrl {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
// роль решает только видимость кнопки «Errors»: доступ к данным проверяет
|
|
28
|
-
// мастер, а auth
|
|
28
|
+
// мастер, а права админа auth-сервис проверяет сам, по нику из
|
|
29
|
+
// VIMP_ADMIN_NICKS
|
|
29
30
|
setAdmin(isAdmin) {
|
|
30
31
|
this._view.setAdmin(isAdmin);
|
|
31
32
|
}
|
|
@@ -42,7 +42,7 @@ export default class GamesCtrl {
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
// роль решает только видимость кнопки модерации: доступ к данным
|
|
45
|
-
// проверяет мастер, а запись — auth
|
|
45
|
+
// проверяет мастер, а запись — auth-сервис, по нику из VIMP_ADMIN_NICKS
|
|
46
46
|
setAdmin(isAdmin) {
|
|
47
47
|
this._view.setAdmin(isAdmin);
|
|
48
48
|
|
|
@@ -3,7 +3,23 @@
|
|
|
3
3
|
let lobbyAuthCtrl;
|
|
4
4
|
|
|
5
5
|
export default class LobbyAuthCtrl {
|
|
6
|
-
|
|
6
|
+
/**
|
|
7
|
+
* @param {Object} model - LobbyAuthModel.
|
|
8
|
+
* @param {Object} view - LobbyAuthView.
|
|
9
|
+
* @param {Object} deps
|
|
10
|
+
* @param {Object} deps.turnstile - client/lib/turnstile.js.
|
|
11
|
+
* @param {Function} [deps.navigate] - переход вкладки на URL.
|
|
12
|
+
*/
|
|
13
|
+
constructor(
|
|
14
|
+
model,
|
|
15
|
+
view,
|
|
16
|
+
{
|
|
17
|
+
turnstile,
|
|
18
|
+
navigate = url => {
|
|
19
|
+
window.location.href = url;
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
) {
|
|
7
23
|
if (lobbyAuthCtrl) {
|
|
8
24
|
return lobbyAuthCtrl;
|
|
9
25
|
}
|
|
@@ -12,30 +28,100 @@ export default class LobbyAuthCtrl {
|
|
|
12
28
|
|
|
13
29
|
this._model = model;
|
|
14
30
|
this._view = view;
|
|
31
|
+
this._turnstile = turnstile;
|
|
32
|
+
this._navigate = navigate;
|
|
33
|
+
this._busy = false;
|
|
15
34
|
|
|
16
35
|
const vp = view.publisher;
|
|
17
36
|
|
|
18
|
-
vp.on('
|
|
19
|
-
vp.on('
|
|
20
|
-
vp.on('
|
|
37
|
+
vp.on('register', 'register', this);
|
|
38
|
+
vp.on('turnstile-retry', 'retryRegister', this);
|
|
39
|
+
vp.on('restore', 'restore', this);
|
|
40
|
+
vp.on('retry', 'retry', this);
|
|
21
41
|
}
|
|
22
42
|
|
|
23
|
-
//
|
|
24
|
-
//
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
43
|
+
// ник проверяется на клиенте до Turnstile и сервера; Turnstile — до
|
|
44
|
+
// запроса регистрации
|
|
45
|
+
async register(rawNick) {
|
|
46
|
+
if (this._busy) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const view = this._view;
|
|
51
|
+
const nick = rawNick.trim();
|
|
52
|
+
|
|
53
|
+
view.renderRegisterError(null);
|
|
54
|
+
view.showTurnstileRetry(false);
|
|
55
|
+
|
|
56
|
+
if (!this._model.validateNick(nick)) {
|
|
57
|
+
view.renderRegisterError('invalidNick');
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
this._busy = true;
|
|
62
|
+
view.setBusy(true);
|
|
63
|
+
|
|
64
|
+
try {
|
|
65
|
+
const { turnstile } = this._model.getClientConfig();
|
|
66
|
+
let token = null;
|
|
67
|
+
|
|
68
|
+
if (turnstile.enabled) {
|
|
69
|
+
try {
|
|
70
|
+
token = await this._turnstile.getToken(
|
|
71
|
+
view.getTurnstileContainer(),
|
|
72
|
+
turnstile.siteKey,
|
|
73
|
+
);
|
|
74
|
+
} catch (error) {
|
|
75
|
+
view.renderRegisterError(
|
|
76
|
+
error?.code === 'expired' ? 'turnstileFailed' : 'turnstileBlocked',
|
|
77
|
+
);
|
|
78
|
+
view.showTurnstileRetry(true);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const ok = await this._model.register(nick, token);
|
|
84
|
+
|
|
85
|
+
// токен Turnstile одноразовый и уже израсходован проверкой на сервере;
|
|
86
|
+
// новый берётся следующей отправкой формы, а не впрок: за 300 с жизни
|
|
87
|
+
// он истёк бы, пока игрок подбирает ник
|
|
88
|
+
if (!ok) {
|
|
89
|
+
this._turnstile.reset();
|
|
90
|
+
}
|
|
91
|
+
} finally {
|
|
92
|
+
this._busy = false;
|
|
93
|
+
view.setBusy(false);
|
|
94
|
+
|
|
95
|
+
// отказ возвращает игрока к полю с его ником
|
|
96
|
+
view.focusNick();
|
|
97
|
+
}
|
|
28
98
|
}
|
|
29
99
|
|
|
30
|
-
|
|
31
|
-
|
|
100
|
+
retryRegister() {
|
|
101
|
+
return this.register(this._view.getNickValue());
|
|
32
102
|
}
|
|
33
103
|
|
|
34
|
-
|
|
35
|
-
|
|
104
|
+
// флаг закрывает только построение URL (хэш verifier считается
|
|
105
|
+
// асинхронно): два построения подряд — лишняя работа. После navigate() он
|
|
106
|
+
// снят — отменённый переход или возврат кнопкой «Назад» не должен
|
|
107
|
+
// оставить экран без регистрации и входа
|
|
108
|
+
async restore(provider) {
|
|
109
|
+
if (this._busy) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
this._busy = true;
|
|
114
|
+
|
|
115
|
+
try {
|
|
116
|
+
this._navigate(await this._model.restoreUrl(provider));
|
|
117
|
+
} catch {
|
|
118
|
+
this._view.renderRestoreError('failed');
|
|
119
|
+
} finally {
|
|
120
|
+
this._busy = false;
|
|
121
|
+
}
|
|
36
122
|
}
|
|
37
123
|
|
|
38
|
-
|
|
39
|
-
this._model.
|
|
124
|
+
retry() {
|
|
125
|
+
return this._model.boot();
|
|
40
126
|
}
|
|
41
127
|
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import Publisher from '../../../lib/Publisher.js';
|
|
2
|
+
|
|
3
|
+
// Singleton ProfileCtrl
|
|
4
|
+
|
|
5
|
+
let profileCtrl;
|
|
6
|
+
|
|
7
|
+
// Контроллер панели профиля: связывает события вида с моделью, как GamesCtrl.
|
|
8
|
+
// Сетевого I/O не делает. Наружу (main.js) пробрасывает 'left' — выход или
|
|
9
|
+
// удаление профиля, после которых вкладка перезагружается
|
|
10
|
+
export default class ProfileCtrl {
|
|
11
|
+
/**
|
|
12
|
+
* @param {Object} model - ProfileModel.
|
|
13
|
+
* @param {Object} view - ProfileView.
|
|
14
|
+
* @param {Object} [deps]
|
|
15
|
+
* @param {Function} [deps.navigate] - уход вкладки на URL провайдера.
|
|
16
|
+
*/
|
|
17
|
+
constructor(
|
|
18
|
+
model,
|
|
19
|
+
view,
|
|
20
|
+
{
|
|
21
|
+
navigate = url => {
|
|
22
|
+
window.location.href = url;
|
|
23
|
+
},
|
|
24
|
+
} = {},
|
|
25
|
+
) {
|
|
26
|
+
if (profileCtrl) {
|
|
27
|
+
return profileCtrl;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
profileCtrl = this;
|
|
31
|
+
|
|
32
|
+
this._model = model;
|
|
33
|
+
this._view = view;
|
|
34
|
+
this._navigate = navigate;
|
|
35
|
+
this._linking = false;
|
|
36
|
+
|
|
37
|
+
this.publisher = new Publisher();
|
|
38
|
+
|
|
39
|
+
const vp = view.publisher;
|
|
40
|
+
const mp = model.publisher;
|
|
41
|
+
|
|
42
|
+
vp.on('open', 'open', this);
|
|
43
|
+
vp.on('link', 'link', this);
|
|
44
|
+
vp.on('unlink', 'unlink', this);
|
|
45
|
+
vp.on('sign-out', 'signOut', this);
|
|
46
|
+
vp.on('export', 'exportData', this);
|
|
47
|
+
vp.on('delete', 'deleteProfile', this);
|
|
48
|
+
|
|
49
|
+
mp.on('left', () => this.publisher.emit('left'));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
open() {
|
|
53
|
+
this._view.show();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// кнопки вида неактивны без согласий; проверка здесь — второй рубеж.
|
|
57
|
+
// Флаг закрывает только построение URL — как у LobbyAuthCtrl.restore()
|
|
58
|
+
async link(provider) {
|
|
59
|
+
if (this._linking || !this._view.consentGiven()) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
this._linking = true;
|
|
64
|
+
|
|
65
|
+
try {
|
|
66
|
+
this._navigate(await this._model.linkUrl(provider));
|
|
67
|
+
} catch {
|
|
68
|
+
this._view.renderError({ area: 'link', code: 'failed' });
|
|
69
|
+
} finally {
|
|
70
|
+
this._linking = false;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
unlink(provider) {
|
|
75
|
+
return this._model.unlink(provider);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
signOut() {
|
|
79
|
+
return this._model.signOut();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
exportData() {
|
|
83
|
+
return this._model.exportData();
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
deleteProfile(confirmNick) {
|
|
87
|
+
return this._model.deleteProfile(confirmNick);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// вкладка вернулась от провайдера: итог привязки показывает сама панель
|
|
91
|
+
openWithLinkResult(result) {
|
|
92
|
+
this._view.show();
|
|
93
|
+
this._view.renderLinkResult(result);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -12,7 +12,7 @@ const REQUEST_FAILED = 'requestFailed';
|
|
|
12
12
|
// вызывающего, очередь модерации и «Test» новой версии. В отличие от
|
|
13
13
|
// LobbyModel сетевые запросы делает сама — это простой REST к мастеру,
|
|
14
14
|
// сигнальный сокет здесь ни при чём (тот же приём, что у LobbyAuthModel с
|
|
15
|
-
//
|
|
15
|
+
// REST к auth-сервису). DOM не трогает.
|
|
16
16
|
export default class GamesModel {
|
|
17
17
|
/**
|
|
18
18
|
* @param {Object} config - Блок `games` конфига лобби (urls, statuses).
|