prepare loader ui
This commit is contained in:
+3
-4
@@ -1,7 +1,6 @@
|
|||||||
package client
|
package client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"image"
|
"image"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
@@ -70,7 +69,7 @@ type client struct {
|
|||||||
hits []world.Hit
|
hits []world.Hit
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewClient(ctx context.Context, s storage.UIStorage, conn connector.UIConnector, app fyne.App) (mc.Client, error) {
|
func NewClient(s storage.UIStorage, conn connector.UIConnector, app fyne.App) (mc.Client, error) {
|
||||||
e := &client{
|
e := &client{
|
||||||
s: s,
|
s: s,
|
||||||
conn: conn,
|
conn: conn,
|
||||||
@@ -85,7 +84,7 @@ func NewClient(ctx context.Context, s storage.UIStorage, conn connector.UIConnec
|
|||||||
hits: make([]world.Hit, 5),
|
hits: make([]world.Hit, 5),
|
||||||
}
|
}
|
||||||
|
|
||||||
e.loadReportFunc = func(t uint) { e.loadReport(ctx, t) }
|
e.loadReportFunc = e.loadReport
|
||||||
|
|
||||||
e.drawer = &world.GGDrawer{DC: nil}
|
e.drawer = &world.GGDrawer{DC: nil}
|
||||||
|
|
||||||
@@ -106,7 +105,7 @@ func NewClient(ctx context.Context, s storage.UIStorage, conn connector.UIConnec
|
|||||||
return e, nil
|
return e, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *client) loadReport(ctx context.Context, t uint) {
|
func (e *client) loadReport(t uint) {
|
||||||
e.conn.FetchReport("GAME_ID", t, func(r report.Report, err error) {
|
e.conn.FetchReport("GAME_ID", t, func(r report.Report, err error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
e.handlerError(err)
|
e.handlerError(err)
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"galaxy/client/loader"
|
"galaxy/client/loader"
|
||||||
|
"galaxy/connector/http"
|
||||||
"galaxy/storage/fs"
|
"galaxy/storage/fs"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
@@ -29,12 +30,16 @@ func main() {
|
|||||||
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
|
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
app := app.NewWithID("galaxy-client")
|
app := app.NewWithID("GalaxyPlus")
|
||||||
s, err := fs.NewFS(app.Storage().RootURI().Path())
|
s, err := fs.NewFS(app.Storage().RootURI().Path())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
l, err := loader.NewLoader(ctx, s, nil, app)
|
c, err := http.NewHttpConnector(ctx, "http://127.0.0.1:8080")
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
l, err := loader.NewLoader(s, c, app)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"galaxy/client"
|
"galaxy/client"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
|
||||||
|
|
||||||
"fyne.io/fyne/v2/app"
|
"fyne.io/fyne/v2/app"
|
||||||
)
|
)
|
||||||
@@ -25,11 +23,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
app := app.New()
|
app := app.New()
|
||||||
|
c, err := client.NewClient(nil, nil, app)
|
||||||
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
c, err := client.NewClient(ctx, nil, nil, app)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
package loader
|
package loader
|
||||||
|
|
||||||
import "galaxy/connector"
|
import (
|
||||||
|
"galaxy/connector"
|
||||||
|
"galaxy/util"
|
||||||
|
)
|
||||||
|
|
||||||
func (l *loader) newerVersion(version string) bool {
|
func (l *loader) newerVersion(version string) bool {
|
||||||
current := l.cli.Version()
|
return util.CompareSemver(util.MustParseSemver(l.client.Version()), util.MustParseSemver(version)) > 0
|
||||||
return compareSemver(current, version) > 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// downloadVersion fetches given version artifact, when newer to the current version,
|
// downloadVersion fetches given version artifact, when newer to the current version,
|
||||||
@@ -13,5 +15,5 @@ func (l *loader) downloadVersion(v connector.VersionInfo) {
|
|||||||
if !l.newerVersion(v.Version) {
|
if !l.newerVersion(v.Version) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
l.conn.DownloadVersion(v.URL)
|
l.connector.DownloadVersion(v.URL)
|
||||||
}
|
}
|
||||||
|
|||||||
+143
-30
@@ -11,18 +11,25 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"fyne.io/fyne/v2"
|
"fyne.io/fyne/v2"
|
||||||
|
"fyne.io/fyne/v2/container"
|
||||||
|
"fyne.io/fyne/v2/widget"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ClientInit func(context.Context, storage.UIStorage, connector.UIConnector, fyne.App) (mc.Client, error)
|
type ClientInit func(storage.UIStorage, connector.UIConnector, fyne.App) (mc.Client, error)
|
||||||
|
|
||||||
type loader struct {
|
type loader struct {
|
||||||
s storage.Storage
|
app fyne.App
|
||||||
conn connector.Connector
|
storage storage.Storage
|
||||||
cli mc.Client
|
connector connector.Connector
|
||||||
|
client mc.Client
|
||||||
|
window fyne.Window
|
||||||
|
textGrid *widget.TextGrid
|
||||||
|
btn *widget.Button
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
clientLibraryFile = "client"
|
pluginInitSymbol = "Factory"
|
||||||
|
libUIPluginFile = "libui"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -30,32 +37,136 @@ var (
|
|||||||
checkVersionTimeout = time.Minute * 60
|
checkVersionTimeout = time.Minute * 60
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewLoader(ctx context.Context, s storage.Storage, conn connector.Connector, app fyne.App) (*loader, error) {
|
func NewLoader(s storage.Storage, conn connector.Connector, app fyne.App) (*loader, error) {
|
||||||
cli, err := loadClientPlugin(ctx, s, conn, app, "./client.so", "Factory")
|
// cli, err := loadClientPlugin(s, conn, app, "./client.so", pluginInitSymbol)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return nil, err
|
// return nil, err
|
||||||
}
|
// }
|
||||||
l := &loader{
|
l := &loader{
|
||||||
conn: conn,
|
app: app,
|
||||||
cli: cli,
|
connector: conn,
|
||||||
s: s,
|
// client: cli,
|
||||||
|
storage: s,
|
||||||
|
textGrid: widget.NewTextGrid(),
|
||||||
|
window: app.NewWindow("Loader"),
|
||||||
}
|
}
|
||||||
|
l.btn = widget.NewButton("OK", l.onButtonAction)
|
||||||
|
l.btn.Disable()
|
||||||
|
|
||||||
|
content := container.NewStack(
|
||||||
|
l.textGrid,
|
||||||
|
container.NewHBox(
|
||||||
|
container.NewCenter(
|
||||||
|
l.btn,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
l.window.SetContent(content)
|
||||||
|
|
||||||
return l, nil
|
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 plugin at path: %s", libUIPluginFile))
|
||||||
|
exists, err := l.storage.FileExists(libUIPluginFile)
|
||||||
|
if err != nil {
|
||||||
|
l.appendFatalError(err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !exists {
|
||||||
|
l.appendText(fmt.Sprintf("plugin not found at %s, fetching available versions", libUIPluginFile))
|
||||||
|
v, err := l.connector.CheckVersion()
|
||||||
|
if err != nil {
|
||||||
|
l.appendFatalError(err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
l.appendText(fmt.Sprintf("received %d versions", len(v)))
|
||||||
|
latest, ok, err := latestVersion(v)
|
||||||
|
if err != nil {
|
||||||
|
l.appendFatalError(err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
l.appendFatalError(errors.New("no latest version available from response"))
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_ = latest
|
||||||
|
} else {
|
||||||
|
l.appendText(fmt.Sprintf("plugin found at %s", libUIPluginFile))
|
||||||
|
}
|
||||||
|
// implement this
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *loader) startLoading() {
|
||||||
|
l.appendText("Loading...")
|
||||||
|
var err error
|
||||||
|
l.client, err = l.initUIPlugin()
|
||||||
|
if err != nil {
|
||||||
|
l.window.Show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *loader) onButtonAction() {
|
||||||
|
l.app.Quit()
|
||||||
|
}
|
||||||
|
|
||||||
func (l *loader) Run(ctx context.Context) error {
|
func (l *loader) Run(ctx context.Context) error {
|
||||||
final := make(chan struct{}, 1)
|
go l.startLoading()
|
||||||
if l.conn != nil {
|
l.app.Run()
|
||||||
go l.backgroundLoop(ctx, final)
|
|
||||||
defer func() { final <- struct{}{} }()
|
// l.window.Show()
|
||||||
}
|
// l.startLoading()
|
||||||
if err := l.cli.Run(); err != nil {
|
|
||||||
return err
|
// select {
|
||||||
}
|
// case <-ctx.Done():
|
||||||
final <- struct{}{}
|
// return nil
|
||||||
|
// case <-l.await:
|
||||||
|
// return nil
|
||||||
|
// }
|
||||||
|
|
||||||
|
// 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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (l *loader) appendText(v string) {
|
||||||
|
fmt.Println(v)
|
||||||
|
fyne.Do(func() { l.textGrid.Append(v) })
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *loader) appendError(err error) {
|
||||||
|
l.appendText(fmt.Sprintf("Error: %s", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *loader) appendFatalError(err error) {
|
||||||
|
l.appendError(err)
|
||||||
|
l.btn.Enable()
|
||||||
|
}
|
||||||
|
|
||||||
func (l *loader) backgroundLoop(ctx context.Context, final <-chan struct{}) {
|
func (l *loader) backgroundLoop(ctx context.Context, final <-chan struct{}) {
|
||||||
checkConnTimer := time.NewTimer(checkConnectionTimeout)
|
checkConnTimer := time.NewTimer(checkConnectionTimeout)
|
||||||
checkVersionTimer := time.NewTimer(checkVersionTimeout)
|
checkVersionTimer := time.NewTimer(checkVersionTimeout)
|
||||||
@@ -66,19 +177,21 @@ func (l *loader) backgroundLoop(ctx context.Context, final <-chan struct{}) {
|
|||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
l.cli.Shutdown()
|
l.client.Shutdown()
|
||||||
return
|
return
|
||||||
case <-final:
|
case <-final:
|
||||||
return
|
return
|
||||||
case <-checkConnTimer.C:
|
case <-checkConnTimer.C:
|
||||||
isGood := l.conn.CheckConnection()
|
isGood := l.connector.CheckConnection()
|
||||||
l.cli.OnConnection(isGood)
|
l.client.OnConnection(isGood)
|
||||||
checkConnTimer.Reset(checkConnectionTimeout)
|
checkConnTimer.Reset(checkConnectionTimeout)
|
||||||
case <-checkVersionTimer.C:
|
case <-checkVersionTimer.C:
|
||||||
versions, err := l.conn.CheckVersion()
|
versions, err := l.connector.CheckVersion()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// propagate error to the UI
|
// propagate error to the UI
|
||||||
} else if latest, ok := latestVersion(versions); ok {
|
} else if latest, ok, err := latestVersion(versions); err != nil {
|
||||||
|
// propagate error to the UI
|
||||||
|
} else if ok {
|
||||||
l.downloadVersion(latest)
|
l.downloadVersion(latest)
|
||||||
}
|
}
|
||||||
checkVersionTimer.Reset(checkVersionTimeout)
|
checkVersionTimer.Reset(checkVersionTimeout)
|
||||||
@@ -86,9 +199,9 @@ func (l *loader) backgroundLoop(ctx context.Context, final <-chan struct{}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// loadClientPlugin loads a Client implementation from a shared object (.so) file at the specified path.
|
// loadClientPlugin loads a Client implementation from a shared plugin file at the specified path.
|
||||||
// It calls the constructor function by name, passing the necessary dependencies, and returns the initialized Client.
|
// It calls the constructor function by name, passing the necessary dependencies, and returns the initialized Client.
|
||||||
func loadClientPlugin(ctx context.Context, s storage.UIStorage, conn connector.UIConnector, app fyne.App, path, name string) (mc.Client, error) {
|
func loadClientPlugin(s storage.UIStorage, conn connector.UIConnector, app fyne.App, path, name string) (mc.Client, error) {
|
||||||
if path == "" {
|
if path == "" {
|
||||||
return nil, errors.New("no plugin path given")
|
return nil, errors.New("no plugin path given")
|
||||||
}
|
}
|
||||||
@@ -108,5 +221,5 @@ func loadClientPlugin(ctx context.Context, s storage.UIStorage, conn connector.U
|
|||||||
return nil, fmt.Errorf("unexpected type %T; want %T", sym, initializerPtr)
|
return nil, fmt.Errorf("unexpected type %T; want %T", sym, initializerPtr)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (*initializerPtr)(ctx, s, conn, app)
|
return (*initializerPtr)(s, conn, app)
|
||||||
}
|
}
|
||||||
|
|||||||
+23
-9
@@ -1,25 +1,39 @@
|
|||||||
package loader
|
package loader
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"galaxy/connector"
|
"galaxy/connector"
|
||||||
|
"galaxy/util"
|
||||||
"runtime"
|
"runtime"
|
||||||
"slices"
|
"slices"
|
||||||
)
|
)
|
||||||
|
|
||||||
func resolvePluginFile(version string) string {
|
func resolvePluginFile(version string) string {
|
||||||
return clientLibraryFile + "-" + version
|
return libUIPluginFile + "-" + version
|
||||||
}
|
}
|
||||||
|
|
||||||
func compareSemver(a, b string) int {
|
// latestVersion should return VersionInfo with the latest Version for the current OD
|
||||||
return 0
|
func latestVersion(versions []connector.VersionInfo) (connector.VersionInfo, bool, error) {
|
||||||
}
|
|
||||||
|
|
||||||
func latestVersion(versions []connector.VersionInfo) (connector.VersionInfo, bool) {
|
|
||||||
os := runtime.GOOS
|
os := runtime.GOOS
|
||||||
versions = slices.DeleteFunc(versions, func(v connector.VersionInfo) bool { return v.OS != os })
|
versions = slices.DeleteFunc(versions, func(v connector.VersionInfo) bool { return v.OS != os })
|
||||||
if len(versions) == 0 {
|
if len(versions) == 0 {
|
||||||
return connector.VersionInfo{}, false
|
return connector.VersionInfo{}, false, nil
|
||||||
}
|
}
|
||||||
slices.SortFunc(versions, func(a, b connector.VersionInfo) int { return compareSemver(b.Version, a.Version) })
|
type v struct {
|
||||||
return versions[0], true
|
vi *connector.VersionInfo
|
||||||
|
sv *util.SemVer
|
||||||
|
}
|
||||||
|
semvers := make([]*v, len(versions))
|
||||||
|
for i := range versions {
|
||||||
|
sv, err := util.ParseSemver(versions[i].Version)
|
||||||
|
if err != nil {
|
||||||
|
return connector.VersionInfo{}, false, fmt.Errorf("latest version: %w", err)
|
||||||
|
}
|
||||||
|
semvers[i] = &v{
|
||||||
|
vi: &versions[i],
|
||||||
|
sv: &sv,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
slices.SortFunc(semvers, func(a, b *v) int { return util.CompareSemver(*b.sv, *a.sv) })
|
||||||
|
return *semvers[0].vi, true, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
package model
|
|
||||||
@@ -98,6 +98,14 @@ func ParseSemver(input string) (SemVer, error) {
|
|||||||
return version, nil
|
return version, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func MustParseSemver(input string) SemVer {
|
||||||
|
if v, err := ParseSemver(input); err != nil {
|
||||||
|
panic(err)
|
||||||
|
} else {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// CompareSemver compares two semantic versions and returns:
|
// CompareSemver compares two semantic versions and returns:
|
||||||
//
|
//
|
||||||
// +1 if x is less than y,
|
// +1 if x is less than y,
|
||||||
|
|||||||
Reference in New Issue
Block a user