diff --git a/client/client.go b/client/client.go index 4885029..4850aad 100644 --- a/client/client.go +++ b/client/client.go @@ -200,6 +200,7 @@ func (e *client) loadWorld(w *world.World) { func (e *client) Run() error { e.BuildUI(e.window) + e.window.SetMaster() e.window.ShowAndRun() e.RequestRefresh() return nil diff --git a/client/loader/loader.go b/client/loader/loader.go index 58bbf1a..ecb0654 100644 --- a/client/loader/loader.go +++ b/client/loader/loader.go @@ -26,6 +26,7 @@ type loader struct { textGrid *widget.TextGrid btn *widget.Button fatalError bool + loaded chan struct{} } const ( @@ -42,10 +43,10 @@ func NewLoader(s storage.Storage, conn connector.Connector, app fyne.App) (*load l := &loader{ app: app, connector: conn, - // client: cli, - storage: s, - textGrid: widget.NewTextGrid(), - window: app.NewWindow("Loader"), + storage: s, + textGrid: widget.NewTextGrid(), + window: app.NewWindow("Loader"), + loaded: make(chan struct{}), } l.btn = widget.NewButton("OK", l.onButtonAction) l.btn.Disable() @@ -63,83 +64,70 @@ func NewLoader(s storage.Storage, conn connector.Connector, app fyne.App) (*load return l, nil } -/* -Комментарий временный, удалить после реализации. - -# Порядок инициализации - -- При ошибках Connector: не фатальное состояние, отображать состояние недоступности сети, предлагать повторить попытку. -- При ошибках Storage: фатальное, отобразить рекомендацию переустановить клиента, выход после confirm от пользователя. - - 1. Проверяем полный путь libUIPluginFile с помощью Storage.FileExists; - - 2. Если libUIPluginFile не существует в Storage: - 2.1. запрашиваем Connector.CheckVersion, - 2.2. с помощью хэлпера latestVersion(...) определяем самую свежую версию; - 2.3. загружаем полученную версию через Connector.DownloadVersion, - 2.4. сохраняем libUIPluginFile в Storage.WriteFile. - - 3. Загружаем libUIPluginFile full path в loadClientPlugin; -*/ -func (l *loader) initUIPlugin() (mc.Client, error) { - // l.appendText(fmt.Sprintf("Checking client plugin at %s", libUIPluginFile)) - // exists, err := l.storage.FileExists(libUIPluginFile) - // if err != nil { - // l.appendFatalError(err) - // return nil, err - // } - // if !exists { - // l.appendText("Client plugin not found, checking available versions") - // v, err := l.connector.CheckVersion() - // if err != nil { - // l.appendError(err) - // return nil, err - // } - // l.appendText(fmt.Sprintf("Received %d versions", len(v))) - // latest, ok, err := latestVersion(v) - // if err != nil { - // l.appendError(err) - // return nil, err - // } - // if !ok { - // l.appendError(errors.New("Server did not responded with a suitable client version")) - // return nil, err - // } - // l.appendText(fmt.Sprintf("Downloading version %s", latest.Version)) - // data, err := l.connector.DownloadVersion(latest.URL) - // if err != nil { - // l.appendError(fmt.Errorf("Version %s download error: %w", latest.Version, err)) - // return nil, err - // } - // err = l.storage.WriteFile(libUIPluginFile, data) - // if err != nil { - // l.appendFatalError(fmt.Errorf("Write plugin file error: %w", err)) - // return nil, err - // } - // } - // l.appendText(fmt.Sprintf("Loading client plugin from %s", libUIPluginFile)) - // cli, err := loadClientPlugin(s, conn, app, "./client.so", pluginInitSymbol) - // if err != nil { - // return nil, err - // } - return nil, nil +func (l *loader) initPlugin() (mc.Client, error) { + exists, pluginPath, err := l.storage.FileExists(libUIPluginFile) + if err != nil { + l.appendFatalError(fmt.Errorf("Client plugin file lookup error: %w", err)) + return nil, err + } + if !exists { + l.logText("Client plugin file not found, fetching available versions") + v, err := l.connector.CheckVersion() + if err != nil { + l.logError(err) + return nil, err + } + l.logText(fmt.Sprintf("Received %d versions", len(v))) + latest, ok, err := latestVersion(v) + if err != nil { + l.logError(err) + return nil, err + } + if !ok { + l.logError(errors.New("Server did not responded with a suitable client version")) + return nil, err + } + l.logText(fmt.Sprintf("Downloading version %s", latest.Version)) + data, err := l.connector.DownloadVersion(latest.URL) + if err != nil { + l.logError(fmt.Errorf("Version %s download error: %w", latest.Version, err)) + return nil, err + } + err = l.storage.WriteFile(libUIPluginFile, data) + if err != nil { + l.appendFatalError(fmt.Errorf("Plugin file write error: %w", err)) + return nil, err + } + } + l.logText(fmt.Sprintf("Loading client plugin from %s", pluginPath)) + cli, err := loadClientPlugin(l.storage, l.connector, l.app, pluginPath, pluginInitSymbol) + if err != nil { + l.appendFatalError(err) + return nil, err + } + return cli, nil } -func (l *loader) startLoading() { +func (l *loader) init() { l.fatalError = false fyne.Do(func() { l.textGrid.SetText("") l.btn.Hide() l.btn.Disable() }) - l.appendText("Loading...") var err error - l.client, err = l.initUIPlugin() + l.client, err = l.initPlugin() + if err == nil { + fyne.Do(func() { + l.window.Hide() + err = l.client.Run() + }) + } if err != nil { fyne.Do(func() { if l.fatalError { l.btn.SetText("Quit") - l.appendText("Please re-install application.") + l.logText("Please re-install application.") } else { l.btn.SetText("Retry") } @@ -147,6 +135,8 @@ func (l *loader) startLoading() { l.btn.Show() l.window.Show() }) + } else { + l.loaded <- struct{}{} } } @@ -154,40 +144,48 @@ func (l *loader) onButtonAction() { if l.fatalError { l.app.Quit() } else { - go l.startLoading() + go l.init() } } -func (l *loader) Run(ctx context.Context) error { - go l.startLoading() - l.app.Run() - - // final := make(chan struct{}, 1) - // if l.connector != nil { - // go l.backgroundLoop(ctx, final) - // defer func() { final <- struct{}{} }() - // } - // if err := l.client.Run(); err != nil { - // return err - // } - // final <- struct{}{} - return nil -} - -func (l *loader) appendText(v string) { - fmt.Println(v) +func (l *loader) logText(v string) { fyne.Do(func() { l.textGrid.Append(v) }) } -func (l *loader) appendError(err error) { - l.appendText(fmt.Sprintf("❌ %s", err)) +func (l *loader) logError(err error) { + l.logText(fmt.Sprintf("❌ %s", err)) } func (l *loader) appendFatalError(err error) { - l.appendError(err) + l.logError(err) l.fatalError = true } +func (l *loader) Run(ctx context.Context) error { + go l.init() + l.app.Run() + select { + case <-ctx.Done(): + return nil + case <-l.loaded: + return l.runClient(ctx) + } +} + +func (l *loader) runClient(ctx context.Context) error { + if l.client == nil { + return errors.New("run: client wasn't initialized, this is an program fatal error.") + } + final := make(chan struct{}, 1) + go l.backgroundLoop(ctx, final) + defer func() { final <- struct{}{} }() + if err := l.client.Run(); err != nil { + return err + } + final <- struct{}{} + return nil +} + func (l *loader) backgroundLoop(ctx context.Context, final <-chan struct{}) { checkConnTimer := time.NewTimer(checkConnectionTimeout) checkVersionTimer := time.NewTimer(checkVersionTimeout) diff --git a/pkg/storage/fs/fs.go b/pkg/storage/fs/fs.go index d34888a..4708e69 100644 --- a/pkg/storage/fs/fs.go +++ b/pkg/storage/fs/fs.go @@ -163,10 +163,10 @@ func (s *fsStorage) SaveOrderAsync(id client.GameID, turn uint, o order.Order, c }() } -func (s *fsStorage) FileExists(path string) (bool, error) { +func (s *fsStorage) FileExists(path string) (bool, string, error) { absPath, err := s.resolvePath(path) if err != nil { - return false, err + return false, "", err } var exists bool @@ -175,7 +175,13 @@ func (s *fsStorage) FileExists(path string) (bool, error) { exists, opErr = s.fileExistsUnlocked(absPath) return opErr }) - return exists, err + if err != nil { + return false, "", err + } + if !exists { + return false, "", nil + } + return true, absPath, nil } func (s *fsStorage) ReadFile(path string) ([]byte, error) { @@ -254,7 +260,8 @@ func (s *fsStorage) ListFiles() ([]string, error) { } func (s *fsStorage) StateExists() (bool, error) { - return s.FileExists(stateFileName) + exists, _, err := s.FileExists(stateFileName) + return exists, err } func (s *fsStorage) LoadState() (client.State, error) { diff --git a/pkg/storage/fs/fs_test.go b/pkg/storage/fs/fs_test.go index 8e44acc..ebec1a3 100644 --- a/pkg/storage/fs/fs_test.go +++ b/pkg/storage/fs/fs_test.go @@ -172,13 +172,28 @@ func TestRawFileCRUDAndList(t *testing.T) { t.Fatalf("write beta: %v", err) } - alphaExists, err := s.FileExists("nested/alpha.txt") + alphaExists, alphaPath, err := s.FileExists("nested/alpha.txt") if err != nil { t.Fatalf("file exists: %v", err) } if !alphaExists { t.Fatal("nested/alpha.txt should exist") } + wantAlphaPath := filepath.Join(s.storageRoot, "nested", "alpha.txt") + if alphaPath != wantAlphaPath { + t.Fatalf("file path = %q, want %q", alphaPath, wantAlphaPath) + } + + missingExists, missingPath, err := s.FileExists("missing.txt") + if err != nil { + t.Fatalf("missing file exists: %v", err) + } + if missingExists { + t.Fatal("missing.txt should not exist") + } + if missingPath != "" { + t.Fatalf("missing file path = %q, want empty string", missingPath) + } alphaData, err := s.ReadFile("nested/alpha.txt") if err != nil { diff --git a/pkg/storage/storage.go b/pkg/storage/storage.go index 68aded8..f5a760f 100644 --- a/pkg/storage/storage.go +++ b/pkg/storage/storage.go @@ -8,7 +8,7 @@ import ( type Storage interface { UIStorage - FileExists(string) (bool, error) + FileExists(string) (bool, string, error) ReadFile(string) ([]byte, error) WriteFile(string, []byte) error DeleteFile(string) error