feat: loader logic

This commit is contained in:
Ilia Denisov
2026-03-15 21:57:42 +02:00
parent 6179dadb5e
commit cc7ecf6667
5 changed files with 117 additions and 96 deletions
+1
View File
@@ -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
+88 -90
View File
@@ -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)