После обновления потребовать переоткрыть БД

oshmianski

Достойный программист
Lotus Team
25.04.2012
711
59
BIT
8
Доброго времени.

Есть подозрение, что пользователи не переоткрывают бд после применения нового шаблона.
Таким образом доработки не "видятся" на клиенте в виду кеширования элементов дизайна.

Подскажите, пожалуйста, есть ли способ таки заставить пользователя переоткрыть бд (и все доки из нее) при изменении шаблона бд?
 

VladSh

начинающий
Lotus Team
11.12.2009
1 797
158
BIT
232
У нас функция есть такая, которая вызывается в начале каждого критического действия - проверяет наличие релиза и предупреждает, что Клиент нужно перезагрузить с закрытием вкладок.
Если надо, сброшу.

P.S. Давно заметил, что если просто перегрузить Клиент без закрытия вкладок, то эффекта это никакого не даст, да коллеги не верили.
Мы даже код такой писали по очистке кеша при перезагрузке, да только он для открытых вкладок всяё равно не чистит кеш.

Недавно на HCL Product Ideas Portal встречал идею о том, чтобы при перезагрузке Клиента и включенной настройке "восстанавливать открытые вкладки" кеш сбрасывался бы. Ну или добавить параметр в notes.ini, чтобы управлять этим процессом, но у них тоже всё годами "на рассмотрении".
 
  • Нравится
Реакции: savl

rinsk

Lotus Team
12.11.2009
1 155
126
BIT
38
У нас функция есть такая, которая вызывается в начале каждого критического действия - проверяет наличие релиза и предупреждает, что Клиент нужно перезагрузить с закрытием вкладок.
Если надо, сброшу.

P.S. Давно заметил, что если просто перегрузить Клиент без закрытия вкладок, то эффекта это никакого не даст, да коллеги не верили.
Мы даже код такой писали по очистке кеша при перезагрузке, да только он для открытых вкладок всяё равно не чистит кеш.

Недавно на HCL Product Ideas Portal встречал идею о том, чтобы при перезагрузке Клиента и включенной настройке "восстанавливать открытые вкладки" кеш сбрасывался бы. Ну или добавить параметр в notes.ini, чтобы управлять этим процессом, но у них тоже всё годами "на рассмотрении".
У меня в таких случаях рестартовал целиком клиент через внешний hta с градусником. Там и кеш грохался.
 

savl

Lotus Team
28.10.2011
2 624
314
BIT
515
Да, если вкладки не закрыть, то соединение восстанавливается, а кэш никуда не уходит. Профайлы так же старые, мы запретили сохранять вкладки.
выставляем через marvel client, даже бесплатная версия позволяет это сделать.
 

rinsk

Lotus Team
12.11.2009
1 155
126
BIT
38
Примерно так :
Visual Basic:
Sub NotesRestart()
    Dim hWnd As Long
    Dim rc As Long
    Dim s As New NotesSession
    Dim st As NotesStream
    Set st=s.Createstream()
    Dim  htaFile, NotesExe, LockFile , nsdRun,hcode, ret
    '''''''
    htaFile=GetNotesProgramDirectory()&"notesrestart.hta"
    NotesExe=GetNotesProgramDirectory()&"nlnotes.exe"
    nsdRun=GetNotesProgramDirectory()&"nsd.exe"
    LockFile=NotesDirData()+"~notes.lck"
    '''''''
    hcode=Replace(HTA_Code(),"%notesexe%",NotesExe)
    hcode=Replace(hcode,"%nsdrun%",NsdRun)
    hcode=Replace(hcode,"%lockfile%",LockFile)
    ''''''
    Call st.Open(htaFile)
    Call st.Truncate()
    Call st.Writetext(hcode)
    Call st.Close()
    ''''''
    ret=Shell(Environ("COMSPEC")+" /c "+Chr(34)+htaFile+Chr(34),7)
    hWnd = FindWindow("Notes",0&)
    rc = PostMessage (hWnd, &H1f,0,0&)
    rc = PostMessage(hWnd, &h10,0,0&)
End Sub

Function HTA_Code() As String
    HTA_Code=|
<html>
 <head>
 <title id="title">Переключение пользователя</title>
 <HTA:APPLICATION
     ID="ProgressBar"
     APPLICATIONNAME="ProgressBar"
     SCROLL="no"
     MAXIMIZEBUTTON="no"
    border="dialog"
     />

 <SCRIPT Language="VBScript">
 
 Public NotesExe
 Public LockFile
 Public nsdRun
 Public x, y, MyTitle
 Public fso,WshShell
 '''''''''''''''''''''
 NotesExe="%notesexe%"
 LockFile="%lockfile%"
 nsdRun="%nsdrun%"

 '''''''''''''''''''''
 Set fso=CreateObject("Scripting.FileSystemObject")
 Set WshShell = CreateObject("WScript.Shell")

 Sub Window_Onload
 window.resizeTo 436,80
 y=50
 MyTitle = " _ Ожидание завершения работы Lotus Notes"
 window.setInterval "Progress", 500
 End Sub
 
 Sub Progress
  if fso.FileExists(LockFile) then ' файл есть - он еще запущен...
  ' удаляем его
    on error resume next
      fso.DeleteFile LockFile,true
      on error goto 0
  x=x+1
  document.Title = FormatPercent(x/y, 0) & MyTitle
  document.all.ProgBarDone.innerText = String(x, "_")
  document.all.ProgBarToDo.innerText = String(y-x, "_") & "*"
   If x=y Then
        MyTitle = " _ Принудительное завершение Lotus Notes"
          document.all.ProgBarToDo.innerText = ""
     x=0
        'MsgBox "ok"
       'window.close
        WshShell.Run chr(34)+NsdRun+chr(34)+" -kill",6
   End If
  else ' файла нет - можно запустить программу...
    WshShell.Run chr(34)+NotesExe+chr(34)
    window.close
  End If
 End Sub
 
 </SCRIPT>
 </head>
 <body bgcolor="#D7D7D7">
 <span id="ProgBarDone" style="background-color: #3399FF"></span>
 <font color="#FFFFFF">
 <span id="ProgBarToDo"style="background-color: #C0C0C0"></span>
 </font>
 </body>
 </html>
|
End Function
 

rinsk

Lotus Team
12.11.2009
1 155
126
BIT
38
Это понятно, можно и так, а как аппликуха автоматически понимает, что дизайн сменился и надо перезапуститься? И в какой точке контроль?
Не не - это просто вариант рестарта клиента с чисткой чего ть:)
В моем случае - это было перекл на нужный Id из ui. Штатный через location оставлял много ошметков..
А так - есть куча способов нотифицировать клиента об необходимости релоада.
 

rinsk

Lotus Team
12.11.2009
1 155
126
BIT
38
вот интересуюсь, как конфетно узнать, что дизайн сменился, изнутри аппликухи)
Я вот щас тестирую схему:
Агент по изменению доков на сервере. Но отрабатывает на view где собран весь дизайн.
Вьюха - отсортирована по дате изменения.
В базе - профильный док с датой последнего изменения дизайна.
Ну далее - понятно: если дизайн поменялся - ставим отметку в профиле.
Эта зараза отрабатывает при любом изменении доков))
Но для моего случая - пофиг в общем то (clear web service cache) .
 

alexas1

Green Team
10.04.2014
1 202
225
BIT
42
....поменялся - ставим отметку в профиле.
Это же не "автоматом" - надо профайл править сначала.... Дальше понятно.
.....
Как меняется дизайн? Штатно, через интерфейс клиента? Агентом?
Это я к чему - я меняю дизайн агентом с полным удалением элементов дизайна и записью новых из темплейта. Вроде, никакого гемора с кешированием элементов дизайна не наблюдал, для такого варианта. Мож чо не видел... (гемор ток один - замена юзинг и эбаут датабэйз)
Но, это апятьжа надо клямкнуть "замени дизайн - новый появился" собственно, тут и клиента переоткрыть не проблема. Но это и не "автоматом"((
 

rinsk

Lotus Team
12.11.2009
1 155
126
BIT
38
Это же не "автоматом" - надо профайл править сначала.... Дальше понятно.
.....
Как меняется дизайн? Штатно, через интерфейс клиента? Агентом?
Это я к чему - я меняю дизайн агентом с полным удалением элементов дизайна и записью новых из темплейта. Вроде, никакого гемора с кешированием элементов дизайна не наблюдал, для такого варианта. Мож чо не видел... (гемор ток один - замена юзинг и эбаут датабэйз)
Но, это апятьжа надо клямкнуть "замени дизайн - новый появился" собственно, тут и клиента переоткрыть не проблема. Но это и не "автоматом"((
Ну при открытии бд или ином событии - проверяется версия в локальном ini и в профиле - и переоткрыть клиента. Автомат:)
В случае замены дизайна агентом совсем шикарно - там и ставить версию.
 

savl

Lotus Team
28.10.2011
2 624
314
BIT
515
Если сделать обновление скриптом. тот же capi, там тоже есть refresh. То в конце работы брать иконку базы и подписать ее.
После этого смотреть дату изменения иконки - таким образом мы нашли дату и время обновления.
Если не ошибаюсь, то пользователь может получить иконку базы через getDocumentByID()

Note IDDatabase Element
FFFF0002"About This Database" document
FFFF0004Default form
FFFF0008Default view
FFFF0010Database icon
FFFF0020Database Design Collection (view)
FFFF0040Database ACL
FFFF0100"Using This Database" document
FFFF0800Replication Formula
 
  • Нравится
Реакции: rinsk

alexas1

Green Team
10.04.2014
1 202
225
BIT
42
Если сделать обновление скриптом. тот же capi, там тоже есть refresh. То в конце работы брать иконку базы и подписать ее.
После этого смотреть дату изменения иконки - таким образом мы нашли дату и время обновления.
Если не ошибаюсь, то пользователь может получить иконку базы через getDocumentByID()

Note IDDatabase Element
FFFF0002"About This Database" document
FFFF0004Default form
FFFF0008Default view
FFFF0010Database icon
FFFF0020Database Design Collection (view)
FFFF0040Database ACL
FFFF0100"Using This Database" document
FFFF0800Replication Formula
Сложно. К тамуж, если менять скриптом, ващще этого ничего не надо - просто переоткрыть по окончании. Если были открыты вкладки, будет гемор, при закрытии могут потеряться изменения. К штатному изменению дизайна это не применимо.
Тут 2 вопроса - что чекать и когда чекать.
Читать .ini - плохо. Читать якобы измененный докдизайн - плохо, нет никакой гарантии получить его уже измененным. Напрашивается решение чекать 100% измененный элемент дизайна, а это только НОВЫЙ док.
Ну, можно в новый дизайн всегда добавлять новую форму с именем типа DesignVxxx. Если получить их коллекцию, то просто ее увеличение будет говорить об изменённом дизайне. Апятьжа, в этой форме можно записывать, что там в дизайне изменено. Да, в принципе, любой элемент дизайна, ток не изменять, а добавлять.
А когда чекать - вопрос открытый.
 

VladSh

начинающий
Lotus Team
11.12.2009
1 797
158
BIT
232
Изначально был чей-то гениальный код, который я допиливал и перепиливал.
Убрал определение БД настроек, так что без напильника не запустится.
Visual Basic:
'/**
'<font color="green">
'<br> Function CheckNewVersion_Continue
'<br> Description: Функция проверки необходимости перезагрузки клиента после релиза
'<br>  
'<br> RESULT description:
'<br> @return
'<br>        False - если продолжение операции недопустимо (требуется перезагрузка клиента); также пользователю выводится диалог и MsgBox
'<br>  
'<br> ЛОГИКА РАБОТЫ:
'<br> Получение из БД настроек данных о последнем релизе (время релиза, список обновленных баз)
'<br> Поиск в notes.ini переменной $NEW_VERSION.
'<br>     ЕСЛИ найдена И время запуска клиента Lotus > времени последнего релиза ТО
'<br>         заменить значение переменной $CUR_VERSION значением переменной $NEW_VERSION;
'<br>         удалить $NEW_VERSION.
'<br>     ИНАЧЕ
'<br>         ЕСЛИ время релиза > переменной $CUR_VERSION ТО
'<br>             ЕСЛИ текущая БД есть в списке обновленных БД ТО
'<br>                 Получить значения полей "информация о нововведениях" и "требуется перезагрузка".
'<br>                 ЕСЛИ нет переменной $NEW_VERSION И поле "информация о нововведениях" не пустое ТО
'<br>                     Отобразить диалог с информацией о нововведениях
'<br>                 ЕСЛИ "требуется перезагрузка" ТО
'<br>                     Время релиза поместить в переменную $NEW_VERSION;
'<br>                 ИНАЧЕ
'<br>                     Время релиза поместить в переменную $CUR_VERSION;
'<br>                     Очистить переменную $NEW_VERSION;
'</font>
'*/
Public Function CheckNewVersion_Continue() As Boolean
    On Error GoTo ErrH
    CheckNewVersion_Continue = True
   
    Const CURVER = "CUR_VERSION"
    Const NEWVER = "NEW_VERSION"
    Const VIEW_RELEASES = "Releases"
   
    Dim ns As New NotesSession
   
    '============================= Получение времени запуска Notes-клиента
    Dim sStartTime As String, dUpTime As NotesDateTime
    sStartTime = ns.GetEnvironmentString("NotesStartTime", False)
    If IsDate(sStartTime) Then
        Set dUpTime = New NotesDateTime(sStartTime)
    Else
        Set dUpTime = New NotesDateTime(Now)
        Dim nUpTimeSeconds As Long
        nUpTimeSeconds = getUptimeSeconds()
        Dim nMinutes As Long
        Dim nHours As Long
        Dim nDays As Long
        'ВАЖНО использование Fix, т.к. Mod округляет операнды
        nMinutes = Fix(nUpTimeSeconds / 60) Mod 60
        Call dUpTime.AdjustMinute(-nMinutes)
        nHours = Fix(nUpTimeSeconds / 3600) Mod 24
        Call dUpTime.AdjustHour(-nHours)
        nDays = Fix(nUpTimeSeconds / 86400) Mod 30
        Call dUpTime.AdjustDay(-nDays)
        If DEBUG_MODE = 1 Then Print "[CheckNewVersion_Continue] UpTime: ", CStr(nUpTimeSeconds)
    End If
    If DEBUG_MODE = 1 Then Print "[CheckNewVersion_Continue] Start time: ", dUpTime.LocalTime
   
    '============================= Получение $CUR_VERSION time
    Dim dCurVerTime As NotesDateTime
    Set dCurVerTime = dtCommonGet(ns.GetEnvironmentString(CURVER, False))
    If dCurVerTime Is Nothing Then
        Set dCurVerTime = dUpTime
        If DEBUG_MODE = 1 Then Print "[CheckNewVersion_Continue] CURVER flag NOT found. Use client's uptime"
    End If
   
    '============================= Получение времени последнего релиза
    Dim ndbConf As NotesDatabase
    'Set ndbConf = ...
    Dim nvSett As NotesView
    Set nvSett = ndbConf.GetView(VIEW_RELEASES)
    Call nvSett.Refresh()
    Dim oNVE As NotesViewEntry
    Set oNVE = nvSett.GetEntryByKey(ns.CurrentDatabase.FileName, False)
    If Not oNVE Is Nothing Then
        Dim dLastRelease As NotesDateTime
        Set dLastRelease = dtCommonGet(CStr(oNVE.ColumnValues(2)))
       
        '========================= Поиск в notes.ini переменной $NEW_VERSION
        Dim bNewVerPresent As Boolean
        'переменная нужна для определения, перезагрузился ли Notes по требованию или нет
        Dim dNewVerTime As NotesDateTime
        Set dNewVerTime = dtCommonGet(ns.GetEnvironmentString(NEWVER, False))
        bNewVerPresent = (Not dNewVerTime Is Nothing)
        If bNewVerPresent Then
            If DEBUG_MODE = 1 Then Print "[CheckNewVersion_Continue] NEWVER flag found"
        End If
       
        If dUpTime.TimeDifference(dLastRelease) > 0 Then
            'Если время запуска клиента Lotus > времени последнего релиза
            If Not bNewVerPresent Then
                'этот кусок выполняется чаще всего
                If dCurVerTime.TimeDifference(dLastRelease) <> 0 Then
                    'если стартовал клиента с утра, а релиз был вчера вечером, то перезагрузка также ненужна
                    Call dtCommonSetVar(CURVER, dLastRelease)
                End If
            Else
                Call dtCommonSetVar(CURVER, dNewVerTime)
                Call setEnvironmentVar(NEWVER, "", False)
                If DEBUG_MODE = 1 Then Print "[CheckNewVersion_Continue] NEWVER is present AND client was rebooted - update local flags"
            End If
        Else
            'Если время запуска клиента Lotus <= времени последнего релиза
            If dLastRelease.TimeDifference(dCurVerTime) > 0 Then
                'Если время релиза > переменной $CUR_VERSION
                If DEBUG_MODE = 1 Then Print "[CheckNewVersion_Continue] Update available (" + dLastRelease.Dateonly + " " + dLastRelease.Timeonly + ")"
               
                'Получаем документ со служебными флагами релиза
                Dim ndSett As NotesDocument
                Set ndSett = oNVE.Document
               
                If DEBUG_MODE = 1 Then Print "[CheckNewVersion_Continue] Updated DBs: ", Join(ndSett.vDBsList, ", ")
                'Если текущая БД есть в списке обновленных БД
                If DEBUG_MODE = 1 Then Print "[CheckNewVersion_Continue] " + ns.CurrentDatabase.FilePath + " database was updated"
                If Not bNewVerPresent And ndSett.whatsNewPresent(0) = "1" Then
                    'Если нет переменной $NEW_VERSION И поле "информация о нововведениях" не пустое
                    If DEBUG_MODE = 1 Then Print "[CheckNewVersion_Continue] Show what's new dialog"
                    'Отобразить диалог с информацией о нововведениях
                    ndSett.subject = "Обновление в ЭДБ от " + dLastRelease.DateOnly + " " + StrLeftBack(dLastRelease.TimeOnly, ":")
                   
                    Dim wrk As New NotesUIWorkspace
                    Call wrk.Dialogbox("Release", True, True, True, True, True, True, "Новое обновление ЭДБ", ndSett, True, False, False)
                End If
                If ndSett.getItemValue("bNeedClientReboot")(0) = "1" Then
                    'Если "требуется перезагрузка"
                    Call dtCommonSetVar(NEWVER, dLastRelease)
                    CheckNewVersion_Continue = False
                    If DEBUG_MODE = 1 Then Print "[CheckNewVersion_Continue] NEED to REBOOT"
                    MsgBox "Обнаружено обновление СЭД." + Chr(13) + Chr(13) + "Для корректной работы в Lotus Вам необходимо ЗАКРЫТЬ ВСЕ ВКЛАДКИ и перезагрузить его.", 48, "Проверка обновлений..."
                Else
                    Call dtCommonSetVar(CURVER, dLastRelease)
                    Call setEnvironmentVar(NEWVER, "", False)
                    If DEBUG_MODE = 1 Then Print "[CheckNewVersion_Continue] Update local flags"
                End If
            End If
        End If
    End If
   
Quit:
    Exit Function
   
ErrH:
    Dim sErrText As String
    sErrText = GetThreadInfo(1) & " (" & Erl & ") -> " & Error$ + " {" & Err & "}"
    Print "ОШИБКА при проверке обновлений: " + sErrText
    Resume Quit
End Function
Visual Basic:
UseLSX "*javacon"

'Общий код ошибок "для пользователя" БЕЗ "обратитесь к ..."
Public Const ERRc1221 = 1221

'общеиспользуемый русский формат
Public Const DT_FORMAT_COMMON = "dd.mm.yyyy hh:nn:ss"

Dim DEBUG_MODE As Integer


%REM
    Sub dtCommonSetVar
    Description: запись в notes.ini даты в общеиспользуемом формате
%END REM
Public Sub dtCommonSetVar(sVarName As String, ndt As NotesDateTime)
    Dim v As Variant
    v = Format$(ndt.LsLocalTime, DT_FORMAT_COMMON)
    Call setEnvironmentVar(sVarName, v, False)
End Sub


%REM
    Function dtCommonGet
    Description: преобразование строки в общеиспользуемом формате в объект NotesDateTime
%END REM
Public Function dtCommonGet(sDtCmnValue As String) As NotesDateTime
    If Len(sDtCmnValue) = 0 Then Exit Function
    Dim vDT As Variant, arrDate As Variant, arrTime As Variant
    vDT = Split(sDtCmnValue, " ")
    arrDate = Split(vDT(0), ".")
    If UBound(arrDate) < 2 Then Exit Function
    If UBound(vDT) <> 0 Then
        arrTime = Split(vDT(1), ":")
        If UBound(arrTime) < 2 Then Exit Function
        vDT = DateNumber(arrDate(2), arrDate(1), arrDate(0)) + TimeNumber(arrTime(0), arrTime(1), arrTime(2))
    Else
        vDT = DateNumber(arrDate(2), arrDate(1), arrDate(0))
    End If
    Set dtCommonGet = New NotesDateTime(CDat(vDT))
End Function


%REM
    Sub setEnvironmentVar
    Description: процедура-обёртка для SetEnvironmentVar
        генерирует правильный текст ошибки
    bIsSystemVar - по умолчанию False
%END REM
Public Sub setEnvironmentVar(sVarName As String, vVarValue As Variant, bIsSystemVar As Boolean)
    On Error GoTo ErrH
   
    Dim s As New NotesSession
    Call s.SetEnvironmentVar(sVarName, vVarValue, bIsSystemVar)
    Exit Sub
   
ErrH:
    Select Case Err
        Case 4412:        'невозможность записи на диск из-за нехватки на нём места
            Error ERRc1221, GetThreadInfo(1) & " (" & Erl & ") -> " + getNoDiskSpaceErrMsg()
        Case 4452:        'попытка записи строки, превышающий лимиты Lotus (вроде как 255 символов)
            Error Err, GetThreadInfo(1) & " (" & Erl & ") -> " & Error$ + ", значение для записи переменной " + sVarName + ": " + vVarValue
        Case Else:
            Error Err, GetThreadInfo(1) & " (" & Erl & ") -> " + Error$
    End Select
End Sub


%REM
    Function getNoDiskSpaceErrMsg
%END REM
Public Function getNoDiskSpaceErrMsg() As String
    Dim ns As New NotesSession
    Dim sDiskName As String
    sDiskName = UCase(StrLeft(ns.GetEnvironmentString("Directory", True), ":"))
    getNoDiskSpaceErrMsg = "Обнаружен сбой в работе клиента Lotus из-за недостаточного места на диске! Почистите диск " + sDiskName + " от ненужных файлов иначе Lotus может работать некорректно!"
End Function


%REM
    Function getJavaClass
    Description: обёртка GetClass, которая подставляет в текст ошибки имя запрашиваемого класса
%END REM
Public Function getJavaClass(sClassName As String) As JavaClass
    On Error GoTo ErrH
    Dim jsession As New JAVASESSION
    Set getJavaClass = jSession.GetClass(sClassName)
Quit:
    Exit Function
ErrH:
    'Err = 304 - не смог инициализировать...
    Error Err, GetThreadInfo(1) & " (" & Erl & ") -> " & Error$ + " " + sClassName
End Function


%REM
    Function getManagementFactory
    Description: отдельная функция для получения ManagementFactory, т.к. это делается из нескольких мест
%END REM
Public Function getManagementFactory() As JavaClass
    Const jPKG_ManagementFactory = "java.lang.management.ManagementFactory"
    Set getManagementFactory = getJavaClass(jPKG_ManagementFactory)
End Function


%REM
    Function getUptimeSeconds
    Created: May 20, 2015
    Description: Функция возвращает время в секундах с момента старта JVM (то есть загрузки клиента или сервера)
%END REM
Public Function getUptimeSeconds() As Long
    getUptimeSeconds = getManagementFactory().CreateObject.getRuntimeMXBean().getUptime() / 1000    
End Function
 
  • Нравится
Реакции: oshmianski

savl

Lotus Team
28.10.2011
2 624
314
BIT
515
Изначально был чей-то гениальный код, который я допиливал и перепиливал.
Убрал определение БД настроек, так что без напильника не запустится.
а в какой момент этот скрипт стартует?
p.s. я тут что подумал... можно же сделать фоновый процесс на capi, который будет стартовать вместе с клиентом, как это marvel client делает и он будет следить за чем-то.
иначе говоря, если пытаться отслеживать обновления на самом клиенте, то это должна быть некая активность: фоновый агент, локальный шедулер, скрипт при открытии бд, функция вызываемая при каждом старте какого-либо действия пользователя.
но по факту, capi поток фоном на клиенте - то что нужно. Ну может rpc на java еще, если плагин сделать и всем установить.
 

oshmianski

Достойный программист
Lotus Team
25.04.2012
711
59
BIT
8
Спасибо всем!

Вижу, что простых способов нет.
 
Мы в соцсетях:

Обучение наступательной кибербезопасности в игровой форме. Начать игру!