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/src/client/style.css
CHANGED
|
@@ -507,12 +507,11 @@ body.hide-cursor {
|
|
|
507
507
|
padding: 5px 0 10px;
|
|
508
508
|
}
|
|
509
509
|
|
|
510
|
-
/* кнопки: провайдеры
|
|
511
|
-
input[type=button], без декоративных стилей, только размер. Create server
|
|
512
|
-
|
|
513
|
-
|
|
510
|
+
/* кнопки: провайдеры Restore, Start, Load more — нативный вид
|
|
511
|
+
input[type=button], без декоративных стилей, только размер. Create server
|
|
512
|
+
сюда не входит: он лежит во флекс-контейнере с gap и центрируется через
|
|
513
|
+
align-self (секция lobby-auth ниже) */
|
|
514
514
|
.lobby-auth-provider,
|
|
515
|
-
#lobby-auth-nick-submit,
|
|
516
515
|
#lobby-more,
|
|
517
516
|
#auth-enter {
|
|
518
517
|
margin: 15px auto;
|
|
@@ -590,6 +589,7 @@ body.hide-cursor {
|
|
|
590
589
|
держится в одном месте (#lobby-footer лежит в секции lobby ниже только
|
|
591
590
|
разметкой) */
|
|
592
591
|
#auth-link,
|
|
592
|
+
#lobby-auth-footer,
|
|
593
593
|
#lobby-footer {
|
|
594
594
|
height: 15px;
|
|
595
595
|
text-shadow: none;
|
|
@@ -602,15 +602,18 @@ body.hide-cursor {
|
|
|
602
602
|
padding: 0 5px;
|
|
603
603
|
}
|
|
604
604
|
|
|
605
|
+
#lobby-auth-footer,
|
|
605
606
|
#lobby-footer {
|
|
606
607
|
font-size: 10px;
|
|
607
608
|
padding: 0;
|
|
608
609
|
}
|
|
609
610
|
|
|
611
|
+
#lobby-auth-footer p,
|
|
610
612
|
#lobby-footer p {
|
|
611
613
|
margin: 0 5px;
|
|
612
614
|
}
|
|
613
615
|
|
|
616
|
+
#lobby-auth-footer p:not(:last-child)::after,
|
|
614
617
|
#lobby-footer p:not(:last-child)::after {
|
|
615
618
|
content: '|';
|
|
616
619
|
padding-left: 10px;
|
|
@@ -625,6 +628,7 @@ body.hide-cursor {
|
|
|
625
628
|
}
|
|
626
629
|
|
|
627
630
|
#auth-link p a,
|
|
631
|
+
#lobby-auth-footer p a,
|
|
628
632
|
#lobby-footer p a {
|
|
629
633
|
color: #ff0;
|
|
630
634
|
}
|
|
@@ -1071,7 +1075,7 @@ body.hide-cursor {
|
|
|
1071
1075
|
display: none;
|
|
1072
1076
|
}
|
|
1073
1077
|
|
|
1074
|
-
/*lobby-auth (
|
|
1078
|
+
/*lobby-auth (экран профиля перед лобби)*/
|
|
1075
1079
|
|
|
1076
1080
|
#lobby-auth {
|
|
1077
1081
|
background-color: #222;
|
|
@@ -1083,33 +1087,141 @@ body.hide-cursor {
|
|
|
1083
1087
|
z-index: 8;
|
|
1084
1088
|
}
|
|
1085
1089
|
|
|
1086
|
-
#lobby-auth-
|
|
1087
|
-
#lobby-auth-
|
|
1090
|
+
#lobby-auth-status,
|
|
1091
|
+
#lobby-auth-welcome {
|
|
1088
1092
|
text-align: center;
|
|
1089
1093
|
}
|
|
1090
1094
|
|
|
1091
|
-
#lobby-auth-
|
|
1095
|
+
#lobby-auth-providers {
|
|
1092
1096
|
display: flex;
|
|
1093
1097
|
flex-direction: column;
|
|
1094
1098
|
gap: 8px;
|
|
1095
1099
|
}
|
|
1096
1100
|
|
|
1097
1101
|
.lobby-auth-provider,
|
|
1098
|
-
#lobby-host
|
|
1099
|
-
#lobby-user-logout {
|
|
1102
|
+
#lobby-host {
|
|
1100
1103
|
align-self: center;
|
|
1101
1104
|
}
|
|
1102
1105
|
|
|
1103
1106
|
#lobby-auth-nick-input {
|
|
1104
1107
|
display: block;
|
|
1105
1108
|
width: 100%;
|
|
1106
|
-
margin
|
|
1109
|
+
margin: 10px 0;
|
|
1110
|
+
box-sizing: border-box;
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
#lobby-auth-nick-error,
|
|
1114
|
+
#lobby-auth-restore-error {
|
|
1115
|
+
text-align: left;
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
#lobby-auth-register,
|
|
1119
|
+
#lobby-auth-restore,
|
|
1120
|
+
#lobby-auth-status-retry,
|
|
1121
|
+
#lobby-auth-turnstile-retry {
|
|
1122
|
+
margin: 8px auto;
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
/* виджет Turnstile шириной 300 px не влезает в карточку 256 px */
|
|
1126
|
+
#lobby-auth-welcome {
|
|
1127
|
+
width: 300px;
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
#lobby-auth-turnstile {
|
|
1131
|
+
display: flex;
|
|
1132
|
+
justify-content: center;
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
/* футер вне рамки: по центру под карточкой */
|
|
1136
|
+
#lobby-auth-footer {
|
|
1137
|
+
justify-content: center;
|
|
1138
|
+
margin: 10px auto 0;
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
/* нейтральная плашка уведомления (профиль не найден, выход, удаление) */
|
|
1142
|
+
.form-notice {
|
|
1143
|
+
background-color: #3d3d3d;
|
|
1144
|
+
border: 1px solid #555;
|
|
1145
|
+
padding: 6px 8px;
|
|
1146
|
+
margin: 8px 0;
|
|
1147
|
+
color: #ddd;
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
.form-notice:empty {
|
|
1151
|
+
display: none;
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
.form-hint {
|
|
1155
|
+
color: #aaa;
|
|
1156
|
+
font-size: 12px;
|
|
1157
|
+
line-height: 16px;
|
|
1158
|
+
margin: 6px 0;
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
/*profile*/
|
|
1162
|
+
|
|
1163
|
+
/* панель профиля (profile.pug): открывается вместо #lobby кнопкой бейджа,
|
|
1164
|
+
каркас — как у панели реестра игр (.card, .lobby-column, .games-head) */
|
|
1165
|
+
#profile-panel {
|
|
1166
|
+
background-color: #222;
|
|
1167
|
+
position: absolute;
|
|
1168
|
+
width: 100%;
|
|
1169
|
+
min-height: 100%;
|
|
1170
|
+
top: 0;
|
|
1171
|
+
left: 0;
|
|
1172
|
+
z-index: 8;
|
|
1173
|
+
display: flex;
|
|
1174
|
+
align-items: flex-start;
|
|
1175
|
+
justify-content: center;
|
|
1176
|
+
box-sizing: border-box;
|
|
1107
1177
|
}
|
|
1108
1178
|
|
|
1109
|
-
#
|
|
1110
|
-
|
|
1111
|
-
|
|
1179
|
+
#profile-panel .card {
|
|
1180
|
+
width: 700px;
|
|
1181
|
+
max-width: 100%;
|
|
1182
|
+
padding: 10px;
|
|
1183
|
+
text-align: left;
|
|
1184
|
+
box-sizing: border-box;
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
#profile-link-benefits {
|
|
1188
|
+
margin: 6px 0;
|
|
1189
|
+
padding-left: 20px;
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
.profile-provider {
|
|
1193
|
+
display: flex;
|
|
1194
|
+
align-items: center;
|
|
1195
|
+
gap: 8px;
|
|
1196
|
+
margin: 6px 0;
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1199
|
+
/* чекбокс и текст в одну строку, текст переносится под себя */
|
|
1200
|
+
#profile-consent label {
|
|
1201
|
+
display: flex;
|
|
1202
|
+
align-items: flex-start;
|
|
1203
|
+
gap: 8px;
|
|
1112
1204
|
margin: 8px 0;
|
|
1205
|
+
font-size: 12px;
|
|
1206
|
+
line-height: 16px;
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
#profile-consent input[type='checkbox'] {
|
|
1210
|
+
flex: none;
|
|
1211
|
+
margin-top: 2px;
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1214
|
+
#profile-consent a {
|
|
1215
|
+
color: #ccc;
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
/* удаление необратимо — блок выделен цветом ошибок */
|
|
1219
|
+
#profile-delete {
|
|
1220
|
+
border: 1px solid #b33;
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
#profile-delete .lobby-section-title {
|
|
1224
|
+
color: #e66;
|
|
1113
1225
|
}
|
|
1114
1226
|
|
|
1115
1227
|
/*games*/
|
|
@@ -1320,7 +1432,7 @@ body.hide-cursor {
|
|
|
1320
1432
|
margin-right: 8px;
|
|
1321
1433
|
}
|
|
1322
1434
|
|
|
1323
|
-
/* кнопки входа в
|
|
1435
|
+
/* кнопки входа в панели стоят в бейдже игрока */
|
|
1324
1436
|
.lobby-user-actions {
|
|
1325
1437
|
display: flex;
|
|
1326
1438
|
gap: 8px;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
//- скрыт по умолчанию: открывается из main.js
|
|
2
|
-
//-
|
|
1
|
+
//- скрыт по умолчанию: открывается из main.js, когда у вкладки есть личность
|
|
2
|
+
//- (экран профиля #lobby-auth, событие LobbyAuthModel 'authenticated')
|
|
3
3
|
div#lobby(style='display:none')
|
|
4
4
|
//- колоночная обёртка: сам #lobby — флекс-строка, центрирующая содержимое,
|
|
5
5
|
//- и футер под панелями иначе встал бы третьей колонкой рядом с ними
|
|
@@ -8,21 +8,21 @@ div#lobby(style='display:none')
|
|
|
8
8
|
//- левая панель: профиль + форма создания комнаты (lobby-page-plan)
|
|
9
9
|
div#lobby-setup-panel.card
|
|
10
10
|
h2= 'VIMP'
|
|
11
|
-
//- бейдж
|
|
12
|
-
//-
|
|
11
|
+
//- бейдж игрока: управляется LobbyAuthView, показывается вместе с
|
|
12
|
+
//- #lobby, когда у вкладки есть личность
|
|
13
13
|
div#lobby-user
|
|
14
14
|
div
|
|
15
|
-
span= '
|
|
15
|
+
span= 'Playing as '
|
|
16
16
|
b#lobby-user-nick
|
|
17
17
|
//- вход в панель реестра игр (master-game-registry, этап 4):
|
|
18
18
|
//- «My games» видит любой авторизованный, «Moderation» — только
|
|
19
19
|
//- админская роль (main.js по LobbyAuthModel.getRole())
|
|
20
20
|
div.lobby-user-actions
|
|
21
|
+
input#profile-open(type='button', value='Profile')
|
|
21
22
|
input#games-open-mine(type='button', value='My games')
|
|
22
23
|
input#games-open-moderation(type='button', value='Moderation', style='display:none')
|
|
23
24
|
//- журнал клиентских ошибок (plan/client-reports, этап 5): только админ
|
|
24
25
|
input#reports-open(type='button', value='Errors', style='display:none')
|
|
25
|
-
input#lobby-user-logout(type='button', value='Sign out')
|
|
26
26
|
hr.lobby-divider
|
|
27
27
|
h3.lobby-section-title= 'HOST A GAME'
|
|
28
28
|
div#lobby-create
|
|
@@ -69,6 +69,11 @@ div#lobby(style='display:none')
|
|
|
69
69
|
//- футер: ссылку и версию пакета движка проставляет LobbyView
|
|
70
70
|
//- (engineVersion.js + lib/packageLink.js)
|
|
71
71
|
div#lobby-footer
|
|
72
|
+
p
|
|
73
|
+
a#lobby-terms-link(target='_blank', rel='noopener') Terms of Service
|
|
74
|
+
| ·
|
|
75
|
+
|
|
|
76
|
+
a#lobby-privacy-link(target='_blank', rel='noopener') Privacy Policy
|
|
72
77
|
p
|
|
73
78
|
a#lobby-link
|
|
74
79
|
p#lobby-version
|
|
@@ -1,19 +1,37 @@
|
|
|
1
|
-
//-
|
|
2
|
-
//-
|
|
3
|
-
//- фильтруются JS по
|
|
1
|
+
//- экран профиля перед лобби: загрузка, отказ сервиса, Welcome (регистрация
|
|
2
|
+
//- ника и восстановление профиля через OAuth). Кнопки провайдеров
|
|
3
|
+
//- фильтруются JS по списку из /client-config
|
|
4
4
|
div#lobby-auth
|
|
5
5
|
|
|
6
|
-
div#lobby-auth-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
div#lobby-auth-login-buttons
|
|
10
|
-
input.lobby-auth-provider#lobby-auth-login-github(type='button', data-provider='github', value='Sign in with GitHub')
|
|
11
|
-
input.lobby-auth-provider#lobby-auth-login-google(type='button', data-provider='google', value='Sign in with Google')
|
|
12
|
-
input.lobby-auth-provider#lobby-auth-login-apple(type='button', data-provider='apple', value='Sign in with Apple')
|
|
13
|
-
div#lobby-auth-login-error
|
|
6
|
+
div#lobby-auth-status.card(style='display:none')
|
|
7
|
+
p#lobby-auth-status-text
|
|
8
|
+
input#lobby-auth-status-retry(type='button', value='Retry', style='display:none')
|
|
14
9
|
|
|
15
|
-
div#lobby-auth-
|
|
16
|
-
h2= '
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
10
|
+
div#lobby-auth-welcome.card(style='display:none')
|
|
11
|
+
h2= 'Welcome to VIMP!'
|
|
12
|
+
p#lobby-auth-notice.form-notice
|
|
13
|
+
input#lobby-auth-nick-input.field-text(type='text', maxlength='15', placeholder='Your nick', autocomplete='off')
|
|
14
|
+
div#lobby-auth-nick-error.form-error
|
|
15
|
+
input#lobby-auth-turnstile-retry(type='button', value='Retry', style='display:none')
|
|
16
|
+
//- контейнер виджета Turnstile: виден, только когда проверке нужен клик
|
|
17
|
+
div#lobby-auth-turnstile
|
|
18
|
+
input#lobby-auth-register(type='button', value='Create profile')
|
|
19
|
+
hr.lobby-divider
|
|
20
|
+
input#lobby-auth-restore(type='button', value='Restore profile')
|
|
21
|
+
div#lobby-auth-providers(style='display:none')
|
|
22
|
+
p= 'Restore a profile linked to:'
|
|
23
|
+
input.lobby-auth-provider(type='button', data-provider='google', value='Continue with Google')
|
|
24
|
+
input.lobby-auth-provider(type='button', data-provider='discord', value='Continue with Discord')
|
|
25
|
+
input.lobby-auth-provider(type='button', data-provider='github', value='Continue with GitHub')
|
|
26
|
+
div#lobby-auth-restore-error.form-error
|
|
27
|
+
|
|
28
|
+
//- футер под карточкой Welcome: ссылки заполняет LobbyAuthView,
|
|
29
|
+
//- версию движка — он же (engineVersion.js)
|
|
30
|
+
div#lobby-auth-footer(style='display:none')
|
|
31
|
+
p
|
|
32
|
+
a#lobby-auth-terms(target='_blank', rel='noopener') Terms of Service
|
|
33
|
+
| ·
|
|
34
|
+
|
|
|
35
|
+
a#lobby-auth-privacy(target='_blank', rel='noopener') Privacy Policy
|
|
36
|
+
p#lobby-auth-version
|
|
37
|
+
p © #{new Date().getFullYear()} VIMP
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
//- Панель профиля в лобби: привязка аккаунта (необязательная), выход,
|
|
2
|
+
//- выгрузка данных, удаление. Скрыта по умолчанию — открывается кнопкой
|
|
3
|
+
//- бейджа пользователя (#profile-open в lobby.pug); id элементов
|
|
4
|
+
//- перечислены в config/lobby.js. Подписи — по-английски, как всё лобби
|
|
5
|
+
div#profile-panel(style='display:none')
|
|
6
|
+
div.lobby-column
|
|
7
|
+
div.games-head
|
|
8
|
+
h3.lobby-section-title= 'Profile'
|
|
9
|
+
input#profile-close(type='button', value='Back to lobby')
|
|
10
|
+
|
|
11
|
+
div.card
|
|
12
|
+
p
|
|
13
|
+
span= 'Nick: '
|
|
14
|
+
b#profile-nick
|
|
15
|
+
p.form-hint= 'The nick cannot be changed.'
|
|
16
|
+
p#profile-status
|
|
17
|
+
|
|
18
|
+
div.card#profile-link
|
|
19
|
+
h3.lobby-section-title= 'Link an account (optional)'
|
|
20
|
+
p= 'Linking is not required — you can keep playing without it. It gives you:'
|
|
21
|
+
ul#profile-link-benefits
|
|
22
|
+
p.form-hint= 'We receive and store only the account ID from the provider — no e-mail, name, avatar or contacts.'
|
|
23
|
+
div#profile-providers
|
|
24
|
+
div#profile-consent
|
|
25
|
+
label
|
|
26
|
+
input#profile-consent-terms(type='checkbox')
|
|
27
|
+
|
|
|
28
|
+
| I have read and agree to the
|
|
29
|
+
|
|
|
30
|
+
a.profile-terms-link(target='_blank', rel='noopener') Terms of Service
|
|
31
|
+
|
|
|
32
|
+
| and the
|
|
33
|
+
|
|
|
34
|
+
a.profile-privacy-link(target='_blank', rel='noopener') Privacy Policy
|
|
35
|
+
| .
|
|
36
|
+
label
|
|
37
|
+
input#profile-consent-data(type='checkbox')
|
|
38
|
+
|
|
|
39
|
+
| I consent to the processing of my personal data — the account ID received from the provider I choose — in order to restore access to my profile, as described in the
|
|
40
|
+
|
|
|
41
|
+
a.profile-privacy-link(target='_blank', rel='noopener') Privacy Policy
|
|
42
|
+
| . I can withdraw this consent at any time by unlinking the account or deleting my profile.
|
|
43
|
+
label
|
|
44
|
+
input#profile-consent-age(type='checkbox')
|
|
45
|
+
|
|
|
46
|
+
| I confirm that I am at least 16 years old, or have reached the age of digital consent in my country if that is higher.
|
|
47
|
+
p#profile-link-notice.form-notice
|
|
48
|
+
div#profile-link-error.form-error
|
|
49
|
+
|
|
50
|
+
div.card#profile-session(style='display:none')
|
|
51
|
+
input#profile-sign-out(type='button', value='Sign out')
|
|
52
|
+
p.form-hint= 'You can sign back in with a linked account.'
|
|
53
|
+
div#profile-session-error.form-error
|
|
54
|
+
|
|
55
|
+
div.card
|
|
56
|
+
input#profile-export(type='button', value='Download my data')
|
|
57
|
+
p.form-hint= 'A JSON file with everything stored about this profile.'
|
|
58
|
+
div#profile-export-error.form-error
|
|
59
|
+
|
|
60
|
+
div.card#profile-delete
|
|
61
|
+
h3.lobby-section-title= 'Delete profile'
|
|
62
|
+
p= 'Deleting the profile is permanent. Your nick, ratings and saved progress in every game will be erased and removed from the leaderboards. The nick will become available to other players.'
|
|
63
|
+
p#profile-delete-admin(style='display:none')= 'Admin profiles cannot be deleted. Remove the nick from the admin list first.'
|
|
64
|
+
input#profile-delete-start(type='button', value='Delete this profile')
|
|
65
|
+
div#profile-delete-confirm(style='display:none')
|
|
66
|
+
p
|
|
67
|
+
span= 'To confirm, type your nick '
|
|
68
|
+
b#profile-delete-nick
|
|
69
|
+
span= ' below:'
|
|
70
|
+
input#profile-delete-input.field-text(type='text', maxlength='15', autocomplete='off')
|
|
71
|
+
input#profile-delete-submit(type='button', value='I understand, delete this profile', disabled)
|
|
72
|
+
input#profile-delete-cancel(type='button', value='Cancel')
|
|
73
|
+
div#profile-delete-error.form-error
|
package/src/config/authClient.js
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
// Конфиг
|
|
2
|
-
//
|
|
3
|
-
// serviceUrl
|
|
4
|
-
//
|
|
1
|
+
// Конфиг клиента профиля лобби. Бандлится в клиентскую сборку, как
|
|
2
|
+
// config/lobby.js — auth-сервис живёт на отдельном домене/порте, поэтому
|
|
3
|
+
// serviceUrl подставляется под конкретный деплой при сборке (см.
|
|
4
|
+
// docs/en/auth.md).
|
|
5
5
|
export default {
|
|
6
6
|
// базовый URL центрального auth-сервиса (packages/auth). Дефолт — dev-порт
|
|
7
7
|
// из packages/auth/src/config/auth.js; в проде подставляется Vite'ом из
|
|
8
8
|
// VITE_AUTH_SERVICE_URL (см. Dockerfile — тот же VIMP_AUTH_SERVICE_URL,
|
|
9
|
-
// что и серверный master/main.js)
|
|
10
|
-
// больше не требуется. Этот модуль импортируется и серверным
|
|
9
|
+
// что и серверный master/main.js). Этот модуль импортируется и серверным
|
|
11
10
|
// master/main.js под чистым Node, где import.meta.env не определён —
|
|
12
11
|
// typeof-guard нужен, иначе TypeError роняет мастер на старте (502)
|
|
13
12
|
serviceUrl:
|
|
@@ -15,41 +14,67 @@ export default {
|
|
|
15
14
|
import.meta.env.VITE_AUTH_SERVICE_URL) ||
|
|
16
15
|
'http://localhost:3010',
|
|
17
16
|
|
|
18
|
-
//
|
|
19
|
-
|
|
17
|
+
// issuer, которым central auth-сервис подписывает identity-токен; должен
|
|
18
|
+
// совпадать с packages/auth/src/config/auth.js: jwt.issuer — хост сверяет
|
|
19
|
+
// claim iss токена с этим значением при проверке подписи по /jwks
|
|
20
|
+
issuer: 'vimp-auth',
|
|
20
21
|
|
|
21
|
-
// ключ
|
|
22
|
-
|
|
22
|
+
// секрет сессии устройства ('s_…') — единственный ключ к профилю: им
|
|
23
|
+
// получают identity-токен и управляют профилем
|
|
24
|
+
sessionStorageKey: 'vimpSession',
|
|
23
25
|
|
|
24
|
-
//
|
|
25
|
-
|
|
26
|
-
// сверяет claim iss токена с этим значением при проверке подписи по /jwks
|
|
27
|
-
issuer: 'vimp-auth',
|
|
26
|
+
// dev-логин auth-сервиса (только dev-сборка): ?devSession=s_…
|
|
27
|
+
devSessionParam: 'devSession',
|
|
28
28
|
|
|
29
|
-
//
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
error: 'authError',
|
|
29
|
+
// возврат с OAuth-провайдера и незавершённая попытка (sessionStorage)
|
|
30
|
+
oauth: {
|
|
31
|
+
codeParam: 'oauthCode',
|
|
32
|
+
errorParam: 'oauthError',
|
|
33
|
+
pendingKey: 'vimpOauthPending',
|
|
35
34
|
},
|
|
36
35
|
|
|
37
|
-
//
|
|
38
|
-
//
|
|
36
|
+
// уведомление, которое должно пережить перезагрузку страницы
|
|
37
|
+
// (sessionStorage)
|
|
38
|
+
noticeKey: 'vimpAuthNotice',
|
|
39
|
+
|
|
40
|
+
// тихое продление identity-токена: за leadMs до exp, повтор при сбое
|
|
41
|
+
// через retryMs
|
|
42
|
+
renew: { leadMs: 3600000, retryMs: 60000 },
|
|
43
|
+
|
|
44
|
+
// Cloudflare Turnstile на регистрации; источник скрипта — тот же, что в
|
|
45
|
+
// CSP мастера (config/master.js security.csp)
|
|
46
|
+
turnstile: {
|
|
47
|
+
scriptUrl:
|
|
48
|
+
'https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit',
|
|
49
|
+
// дольше не ждём: скрипт, скорее всего, заблокирован расширением
|
|
50
|
+
loadTimeoutMs: 4000,
|
|
51
|
+
action: 'register',
|
|
52
|
+
},
|
|
53
|
+
|
|
54
|
+
// DOM-элементы экрана профиля (views/includes/lobbyAuth.pug) и бейджа
|
|
55
|
+
// игрока в лобби (views/includes/lobby.pug)
|
|
39
56
|
elems: {
|
|
40
57
|
containerId: 'lobby-auth',
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
58
|
+
statusId: 'lobby-auth-status',
|
|
59
|
+
statusTextId: 'lobby-auth-status-text',
|
|
60
|
+
statusRetryId: 'lobby-auth-status-retry',
|
|
61
|
+
welcomeId: 'lobby-auth-welcome',
|
|
62
|
+
footerId: 'lobby-auth-footer',
|
|
63
|
+
noticeId: 'lobby-auth-notice',
|
|
44
64
|
nickInputId: 'lobby-auth-nick-input',
|
|
45
65
|
nickErrorId: 'lobby-auth-nick-error',
|
|
46
|
-
|
|
66
|
+
turnstileRetryId: 'lobby-auth-turnstile-retry',
|
|
67
|
+
turnstileId: 'lobby-auth-turnstile',
|
|
68
|
+
registerId: 'lobby-auth-register',
|
|
69
|
+
termsId: 'lobby-auth-terms',
|
|
70
|
+
privacyId: 'lobby-auth-privacy',
|
|
71
|
+
versionId: 'lobby-auth-version',
|
|
72
|
+
restoreId: 'lobby-auth-restore',
|
|
73
|
+
providersId: 'lobby-auth-providers',
|
|
74
|
+
restoreErrorId: 'lobby-auth-restore-error',
|
|
75
|
+
providerClass: 'lobby-auth-provider',
|
|
47
76
|
lobbyId: 'lobby',
|
|
48
77
|
userId: 'lobby-user',
|
|
49
78
|
userNickId: 'lobby-user-nick',
|
|
50
|
-
userLogoutId: 'lobby-user-logout',
|
|
51
79
|
},
|
|
52
|
-
|
|
53
|
-
// CSS-класс кнопок провайдеров в lobbyAuth.pug (data-provider на каждой)
|
|
54
|
-
providerButtonClass: 'lobby-auth-provider',
|
|
55
80
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Английские тексты движковых кодов чата по умолчанию (группы s, v, m, c
|
|
1
|
+
// Английские тексты движковых кодов чата по умолчанию (группы s, v, m, c);
|
|
2
2
|
// индекс = номер кода. Лежат здесь, а не в clientDefaults: при слиянии
|
|
3
3
|
// конфига массивы заменяются целиком, и массив `s` игры стёр бы умолчания.
|
|
4
4
|
// Правило перекрытия: строка игры по тому же индексу важнее умолчания;
|
|
@@ -41,5 +41,4 @@ export default {
|
|
|
41
41
|
],
|
|
42
42
|
m: ['Current map: {0}', 'Next map: {0}'],
|
|
43
43
|
c: ['Command not found', 'Your rank: {0}'],
|
|
44
|
-
n: ['Invalid name', '{0} changed name to {1}'],
|
|
45
44
|
};
|
package/src/config/env.js
CHANGED
|
@@ -20,8 +20,8 @@ export function applyMasterEnv(config, env = process.env) {
|
|
|
20
20
|
config.set('master:port', Number(env.VIMP_MASTER_PORT));
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
// домен central auth-сервиса
|
|
24
|
-
//
|
|
23
|
+
// домен central auth-сервиса — попадает в CSP connect-src, т.к. лобби
|
|
24
|
+
// делает туда прямые fetch (ручки профиля)
|
|
25
25
|
if (env.VIMP_AUTH_SERVICE_URL) {
|
|
26
26
|
config.set('master:security:authServiceUrl', env.VIMP_AUTH_SERVICE_URL);
|
|
27
27
|
}
|
package/src/config/lobby.js
CHANGED
|
@@ -213,7 +213,8 @@ export default {
|
|
|
213
213
|
// подключён по WebRTC; при смене состава отчёт уходит сразу, с дебаунсом);
|
|
214
214
|
// minTokenLifetimeMs — вкладка, чей вход истечёт раньше, не объявляет
|
|
215
215
|
// canHost и отказывается от промоушена (токен предъявляется мастеру
|
|
216
|
-
// посреди
|
|
216
|
+
// посреди матча; обычно его заранее продлевает TokenRenewer, порог работает,
|
|
217
|
+
// пока продление не удаётся); tokenHandoffLeadMs — за столько до
|
|
217
218
|
// истечения входа хост с бетой сам передаёт роль (плановая передача на
|
|
218
219
|
// границе раунда); tokenHandoffRetryMs — через сколько повторить её, если
|
|
219
220
|
// беты ещё нет, идёт эстафета Worker'ов или передача сорвалась (повторы —
|
|
@@ -445,6 +446,49 @@ export default {
|
|
|
445
446
|
},
|
|
446
447
|
},
|
|
447
448
|
|
|
449
|
+
// панель профиля (profile.pug): привязка OAuth, выход, выгрузка данных,
|
|
450
|
+
// удаление. Открывается кнопкой бейджа и прячет #lobby, как панель игр
|
|
451
|
+
profile: {
|
|
452
|
+
// подписи провайдеров в интерфейсе
|
|
453
|
+
providerTitles: { google: 'Google', discord: 'Discord', github: 'GitHub' },
|
|
454
|
+
// имя файла выгрузки данных
|
|
455
|
+
exportFileName: nick => `vimp-profile-${nick}.json`,
|
|
456
|
+
elems: {
|
|
457
|
+
panelId: 'profile-panel',
|
|
458
|
+
lobbyId: 'lobby',
|
|
459
|
+
openBtnId: 'profile-open',
|
|
460
|
+
closeBtnId: 'profile-close',
|
|
461
|
+
nickId: 'profile-nick',
|
|
462
|
+
statusId: 'profile-status',
|
|
463
|
+
benefitsId: 'profile-link-benefits',
|
|
464
|
+
providersId: 'profile-providers',
|
|
465
|
+
consentId: 'profile-consent',
|
|
466
|
+
consentTermsId: 'profile-consent-terms',
|
|
467
|
+
consentDataId: 'profile-consent-data',
|
|
468
|
+
consentAgeId: 'profile-consent-age',
|
|
469
|
+
termsLinkClass: 'profile-terms-link',
|
|
470
|
+
privacyLinkClass: 'profile-privacy-link',
|
|
471
|
+
linkNoticeId: 'profile-link-notice',
|
|
472
|
+
linkErrorId: 'profile-link-error',
|
|
473
|
+
sessionCardId: 'profile-session',
|
|
474
|
+
signOutBtnId: 'profile-sign-out',
|
|
475
|
+
sessionErrorId: 'profile-session-error',
|
|
476
|
+
exportBtnId: 'profile-export',
|
|
477
|
+
exportErrorId: 'profile-export-error',
|
|
478
|
+
deleteAdminNoteId: 'profile-delete-admin',
|
|
479
|
+
deleteStartBtnId: 'profile-delete-start',
|
|
480
|
+
deleteConfirmId: 'profile-delete-confirm',
|
|
481
|
+
deleteNickId: 'profile-delete-nick',
|
|
482
|
+
deleteInputId: 'profile-delete-input',
|
|
483
|
+
deleteSubmitBtnId: 'profile-delete-submit',
|
|
484
|
+
deleteCancelBtnId: 'profile-delete-cancel',
|
|
485
|
+
deleteErrorId: 'profile-delete-error',
|
|
486
|
+
// ссылки на юридические страницы в футере лобби
|
|
487
|
+
footerTermsId: 'lobby-terms-link',
|
|
488
|
+
footerPrivacyId: 'lobby-privacy-link',
|
|
489
|
+
},
|
|
490
|
+
},
|
|
491
|
+
|
|
448
492
|
// журнал клиентских ошибок (plan/client-reports, этап 5): только админ.
|
|
449
493
|
// URL и id элементов — здесь, как у панели реестра игр (reports.pug)
|
|
450
494
|
clientReports: {
|
package/src/config/master.js
CHANGED
|
@@ -218,10 +218,12 @@ export default {
|
|
|
218
218
|
// политики; мастер применяет её к своим ответам только в проде (в dev CSP
|
|
219
219
|
// сломала бы Vite HMR). WASM требует 'wasm-unsafe-eval', Worker — 'blob:';
|
|
220
220
|
// connect-src data: — PixiJS фетчит тестовый data:-URL для проверки ImageBitmap.
|
|
221
|
-
// authServiceUrl
|
|
222
|
-
//
|
|
223
|
-
// его разрешать; сам OAuth-редирект (location.href на
|
|
224
|
-
// CSP не ограничивает — это навигация верхнего
|
|
221
|
+
// authServiceUrl — домен central auth-сервиса (packages/auth): лобби
|
|
222
|
+
// делает туда прямой fetch (браузерные ручки auth-сервиса), поэтому
|
|
223
|
+
// connect-src должен его разрешать; сам OAuth-редирект (location.href на
|
|
224
|
+
// auth-сервис/провайдера) CSP не ограничивает — это навигация верхнего
|
|
225
|
+
// уровня, не fetch/XHR. challenges.cloudflare.com в script-src и frame-src
|
|
226
|
+
// — скрипт и iframe виджета Turnstile на экране регистрации.
|
|
225
227
|
// script-src несёт sha256-хэш инлайнового importmap из index.html (для
|
|
226
228
|
// pixi.js — src="..." на <script type="importmap"> браузеры не
|
|
227
229
|
// поддерживают, инлайн обязателен). Хэш посчитан по факту собранного
|
|
@@ -235,8 +237,9 @@ export default {
|
|
|
235
237
|
csp: authServiceUrl =>
|
|
236
238
|
[
|
|
237
239
|
"default-src 'self'",
|
|
238
|
-
"script-src 'self' 'wasm-unsafe-eval' 'sha256-XJmzkFBLHYpcM8KgGRFztTJTwfMb5xIFKAmqlgTpobo='",
|
|
240
|
+
"script-src 'self' 'wasm-unsafe-eval' 'sha256-XJmzkFBLHYpcM8KgGRFztTJTwfMb5xIFKAmqlgTpobo=' https://challenges.cloudflare.com",
|
|
239
241
|
"worker-src 'self' blob:",
|
|
242
|
+
'frame-src https://challenges.cloudflare.com',
|
|
240
243
|
`connect-src 'self' wss: data:${authServiceUrl ? ` ${authServiceUrl}` : ''}`,
|
|
241
244
|
"img-src 'self' data: blob:",
|
|
242
245
|
"style-src 'self' 'unsafe-inline'",
|
package/src/config/wsports.js
CHANGED
|
@@ -19,7 +19,6 @@ export default {
|
|
|
19
19
|
SOUND_DATA: 6,
|
|
20
20
|
GAME_INFORM_DATA: 7,
|
|
21
21
|
TECH_INFORM_DATA: 8,
|
|
22
|
-
MISC: 9,
|
|
23
22
|
PING: 10,
|
|
24
23
|
CLEAR: 11,
|
|
25
24
|
CONSOLE: 12,
|
|
@@ -51,5 +50,9 @@ export default {
|
|
|
51
50
|
RESUME_REQUEST: 9,
|
|
52
51
|
// игрок уходит сам: хост снимает его сразу, без ожидания возврата
|
|
53
52
|
LEAVE: 10,
|
|
53
|
+
// свежий identity-токен игрока: клиент продлевает JWT молча, а хост пишет
|
|
54
|
+
// rank/state его токеном — без порта долгий матч писал бы истёкшим.
|
|
55
|
+
// Прежний хост порт игнорирует
|
|
56
|
+
TOKEN_REFRESH: 11,
|
|
54
57
|
},
|
|
55
58
|
};
|