fix errors on insertion to game, fix db creation of game

This commit is contained in:
lbenedar
2026-04-23 19:03:56 +03:00
parent f46ff8333e
commit 07025afefc
36 changed files with 1820 additions and 693 deletions

View File

@@ -3,7 +3,8 @@ package actions
import "errors"
var (
ErrActionNotFound = errors.New("Action doesn't found")
ErrObjTypeNotMatch = errors.New("Provided value type didn't match obj field type")
ErrObjNotPointer = errors.New("Passed obj is not pointer")
ErrActionNotFound = errors.New("Action doesn't found")
ErrObjTypeNotMatch = errors.New("Provided value type didn't match obj field type")
ErrObjNotPointer = errors.New("Passed obj is not pointer")
ErrUserChannelIsClosed = errors.New("User channel is closed")
)

View File

@@ -1,6 +1,8 @@
package actions
import (
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/temp_models/db"
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/temp_models/json"
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/transport"
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/types"
)
@@ -22,8 +24,18 @@ func (msg WsSessionMsg) Action(tr *transport.FoundryTransport, status *types.Fou
close(tr.ReadChan.Reconnect())
return nil
}
// tr.Logger.Warn("World is started. Please, return to setup page to fully initialize database")
// go msg.OnActiveWorld(tr)
// select {
// case <-tr.LoggedInChan:
// return ErrUserChannelIsClosed
// default:
// tr.LoggedInChan <- true
// }
// time.Sleep(3 * time.Second)
// types.CloseChannel(tr.LoggedInChan)
go msg.OnActiveWorld(tr)
return nil
}
@@ -37,13 +49,27 @@ func (msg WsSessionMsg) Action(tr *transport.FoundryTransport, status *types.Fou
}
func (msg WsSessionMsg) OnActiveWorld(tr *transport.FoundryTransport) error {
wsMsg := types.NewWsMessage("world", tr.CurrWsId)
tr.CurrWsId++
answer, err := tr.HandleWebsocketRequest(wsMsg)
msgJson, err := tr.GetJsonData("world")
if err != nil {
return err
}
tr.Logger.Info("World data", "answer", string(answer))
tr.Logger.Info("Game data received")
gameJson, err := json.ParseGame(msgJson)
if err != nil {
return err
}
var foundryStateDb db.Game
gameJson.ToDB(&foundryStateDb)
err = foundryStateDb.Insert(tr.DB)
if err != nil {
tr.ReadChan.Err() <- &types.FoundryError{Direction: types.WriterCode, Err: err, Type: types.DbCode}
}
tr.Logger.Info("Game data succesfully inserted to DB")
return nil
}

View File

@@ -1,5 +1,15 @@
package db
import (
"context"
"database/sql"
"errors"
"fmt"
"github.com/jmoiron/sqlx"
"golang.org/x/sync/errgroup"
)
type Actor struct {
ID string
@@ -8,8 +18,60 @@ type Actor struct {
Type string
Folder string
Sort int
PrototypeToken Token
PrototypeToken *Token
Stats Stats
Items []*Item
Ownership []OwnershipString
}
func (a *Actor) Query(data *InsertId[uint]) {
data.query = fmt.Sprintf(`
INSERT INTO actor (%s, id, img, name, type, folder, sort)
VALUES ($1, $2, $3, $4, $5, $6, $7)`, data.fieldName)
}
func (a *Actor) InsertObjects(tx *sqlx.Tx) error {
group, ctx := errgroup.WithContext(context.Background())
relId := InsertId[string]{id: a.ID, fieldName: "actor_id"}
InsertWithCtxParallel(group, ctx, tx, a.PrototypeToken, relId)
InsertWithCtxParallel(group, ctx, tx, a.Stats, relId)
InsertSliceParallel(group, tx, a.Ownership, relId)
InsertSliceParallel(group, tx, a.Items, relId)
err := group.Wait()
if err != nil && !errors.Is(err, sql.ErrNoRows) {
return err
}
return nil
}
func (a *Actor) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, a.ID, a.Img, a.Name, a.Type, a.Folder, a.Sort}
_, err := tx.Exec(data.query, args...)
if err != nil {
return err
}
return a.InsertObjects(tx)
}
func (a *Actor) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, a.ID, a.Img, a.Name, a.Type, a.Folder, a.Sort}
_, err := tx.ExecContext(ctx, data.query, args...)
if err != nil {
return err
}
return a.InsertObjects(tx)
}

View File

@@ -23,7 +23,7 @@ type CardDeck struct {
Sort int
DisplayCount bool
Stats Stats
Ownership Ownership
Ownership []OwnershipString
Cards []*Card
}
@@ -38,7 +38,7 @@ func (c *CardDeck) InsertObjects(tx *sqlx.Tx) error {
relId := InsertId[string]{id: c.ID, fieldName: "card_deck_id"}
InsertWithCtxParallel(group, ctx, tx, c.Stats, relId)
InsertWithCtxParallel(group, ctx, tx, c.Ownership, relId)
InsertSliceParallel(group, tx, c.Ownership, relId)
InsertSliceParallel(group, tx, c.Cards, relId)
err := group.Wait()

View File

@@ -41,9 +41,9 @@ type Game struct {
Items []*Item
Settings []*Setting
Journals []*Journal
Tables []*Table //
Tables []*Table
Playlists []*Playlist
Actors []Actor //
Actors []*Actor
// Scenes []Scene
}
@@ -62,22 +62,25 @@ func (g *Game) InsertObjects(tx *sqlx.Tx) error {
InsertWithCtxParallel(group, ctx, tx, g.World, relDataString)
InsertWithCtxParallel(group, ctx, tx, g.System, relDataString)
InsertSimpleSliceParallel(group, tx, g.ActiveUsers, &InsertId[uint]{id: g.ID, fieldName: "game_id", tableName: "active_users"})
InsertSimpleSliceParallel(group, tx, g.ActiveUsers,
&InsertId[uint]{id: g.ID, fieldName: "game_id", tableName: "active_users"})
InsertSliceParallel(group, tx, g.Modules, relData)
InsertSliceParallel(group, tx, g.PackageWarnings, relData)
InsertSliceParallel(group, tx, g.Packs, relDataString)
InsertSliceParallel(group, tx, g.Messages, relData)
InsertSliceParallel(group, tx, g.Combats, relData)
InsertSliceParallel(group, tx, g.CardDeck, relData)
InsertSliceParallel(group, tx, g.Users, relData)
InsertSliceParallel(group, tx, g.Macros, relData)
InsertSliceParallel(group, tx, g.Folders, relData)
InsertSliceParallel(group, tx, g.Items, relData)
InsertSliceParallel(group, tx, g.Settings, relData)
InsertSliceParallel(group, tx, g.Journals, relData)
InsertSliceParallel(group, tx, g.Tables, relData)
InsertSliceParallel(group, tx, g.Playlists, relData)
// InsertSliceParallel(group, tx, g.Messages, relData)
// InsertSliceParallel(group, tx, g.Combats, relData)
// InsertSliceParallel(group, tx, g.CardDeck, relData)
// InsertSliceParallel(group, tx, g.Users, relData)
// InsertSliceParallel(group, tx, g.Macros, relData)
// InsertSliceParallel(group, tx, g.Folders, relData)
// InsertSliceParallel(group, tx, g.Items,
// InsertId[string]{id: strconv.FormatUint(uint64(g.ID), 10), fieldName: "game_id"})
// InsertSliceParallel(group, tx, g.Settings, relData)
// InsertSliceParallel(group, tx, g.Journals, relData)
// InsertSliceParallel(group, tx, g.Tables, relData)
// InsertSliceParallel(group, tx, g.Playlists, relData)
// InsertSliceParallel(group, tx, g.Actors, relData)
err := group.Wait()
if err != nil && !errors.Is(err, sql.ErrNoRows) {

View File

@@ -15,14 +15,14 @@ type Index struct {
Type string
}
func (i *Index) Query(data *InsertId[uint]) {
func (i *Index) Query(data *InsertId[string]) {
data.query = `
INSERT INTO index_ (pack_id, id, folder, img, name, type)
VALUES ($1, $2, $3, $4, $5, $6)
RETURNING id`
}
func (i *Index) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
func (i *Index) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}
@@ -37,7 +37,7 @@ func (i *Index) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
return nil
}
func (i *Index) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
func (i *Index) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}

View File

@@ -22,7 +22,7 @@ type Item struct {
Ownership []OwnershipString
}
func (i *Item) Query(data *InsertId[uint]) {
func (i *Item) Query(data *InsertId[string]) {
data.query = fmt.Sprintf(`
INSERT INTO world_folder (%s, id, img, name, type, folder, sort)
VALUES ($1, $2, $3, $4, $5, $6, $7)
@@ -43,7 +43,7 @@ func (i *Item) InsertObjects(tx *sqlx.Tx) error {
return nil
}
func (i *Item) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
func (i *Item) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}
@@ -58,7 +58,7 @@ func (i *Item) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
return i.InsertObjects(tx)
}
func (i *Item) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
func (i *Item) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}

View File

@@ -1,5 +1,15 @@
package db
import (
"context"
"database/sql"
"errors"
"fmt"
"github.com/jmoiron/sqlx"
"golang.org/x/sync/errgroup"
)
type Light struct {
ID uint
@@ -20,6 +30,60 @@ type Light struct {
LightDarkness LightDarkness
}
func (l *Light) Query(data *InsertId[uint]) {
data.query = fmt.Sprintf(`
INSERT INTO token_light (%s, color, priority, angle, negative, alpha, bright, coloration, dim,
attenuation, luminosity, saturation, contrast, shadows)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)
RETURNING id`, data.fieldName)
}
func (l *Light) InsertObjects(tx *sqlx.Tx) error {
group, ctx := errgroup.WithContext(context.Background())
relId := InsertId[uint]{id: l.ID, fieldName: "token_light_id"}
InsertWithCtxParallel(group, ctx, tx, l.LightAnimation, relId)
InsertWithCtxParallel(group, ctx, tx, l.LightDarkness, relId)
err := group.Wait()
if err != nil && !errors.Is(err, sql.ErrNoRows) {
return err
}
return nil
}
func (l *Light) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, l.Color, l.Priority, l.Angle, l.Negative, l.Alpha, l.Bright, l.Coloration, l.Dim,
l.Attenuation, l.Luminosity, l.Saturation, l.Contrast, l.Shadows}
err := tx.QueryRowx(data.query, args...).Scan(&l.ID)
if err != nil {
return err
}
return nil
}
func (l *Light) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, l.Color, l.Priority, l.Angle, l.Negative, l.Alpha, l.Bright, l.Coloration, l.Dim,
l.Attenuation, l.Luminosity, l.Saturation, l.Contrast, l.Shadows}
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&l.ID)
if err != nil {
return err
}
return nil
}
type LightAnimation struct {
ID uint
@@ -27,9 +91,84 @@ type LightAnimation struct {
Intensity int
Reverse bool
}
func (l LightAnimation) Query(data *InsertId[uint]) {
data.query = fmt.Sprintf(`
INSERT INTO token_light_animation (%s, speed, intensity, reverse)
VALUES ($1, $2, $3, $4)
RETURNING id`, data.fieldName)
}
func (l LightAnimation) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, l.Speed, l.Intensity, l.Reverse}
err := tx.QueryRowx(data.query, args...).Scan(&l.ID)
if err != nil {
return err
}
return nil
}
func (l LightAnimation) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, l.Speed, l.Intensity, l.Reverse}
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&l.ID)
if err != nil {
return err
}
return nil
}
type LightDarkness struct {
ID uint
Min float64
Max float64
}
func (l LightDarkness) Query(data *InsertId[uint]) {
data.query = fmt.Sprintf(`
INSERT INTO token_light_darkness (%s, min, max)
VALUES ($1, $2, $3, $4)
RETURNING id`, data.fieldName)
}
func (l LightDarkness) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, l.Min, l.Max}
err := tx.QueryRowx(data.query, args...).Scan(&l.ID)
if err != nil {
return err
}
return nil
}
func (l LightDarkness) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, l.Min, l.Max}
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&l.ID)
if err != nil {
return err
}
return nil
}

View File

@@ -4,7 +4,7 @@ import (
"context"
"database/sql"
"errors"
"fmt"
"strings"
"github.com/jmoiron/sqlx"
"golang.org/x/sync/errgroup"
@@ -49,12 +49,19 @@ type Module struct {
}
func (m *Module) Query(data *InsertId[uint]) {
data.query = fmt.Sprintf(`
INSERT INTO module (%s, id, title, description, url, license, readme, bugs,
data.query = `
INSERT INTO module (setup_id, id, title, description, url, license, readme, bugs,
changelog, version, manifest, download, socket, protected, exclusive_, persistent_storage,
core_translation, library, locked, owned, has_storage, active, availability)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17,
$18, $19, $20, $21, $22, $23)`, data.fieldName)
$18, $19, $20, $21, $22, $23)
ON CONFLICT(id) DO NOTHING`
}
func (m *Module) ConnectGameQuery(data *InsertId[uint]) {
data.query = `
INSERT INTO game_to_module (game_id, module_id)
VALUES ($1, $2)`
}
func (m *Module) InsertObjects(tx *sqlx.Tx) error {
@@ -65,19 +72,19 @@ func (m *Module) InsertObjects(tx *sqlx.Tx) error {
InsertWithCtxParallel(group, ctx, tx, m.Relationships, relId)
InsertWithCtxParallel(group, ctx, tx, m.Compatibility, relId)
// scriptRelId := &InsertId[string]{id: m.ID, fieldName: "module_id", tableName: "scripts"}
// InsertSimpleSliceParallel(group, tx, m.Scripts, scriptRelId)
// esModulesRelId := &InsertId[string]{id: m.ID, fieldName: "module_id", tableName: "es_modules"}
// InsertSimpleSliceParallel(group, tx, m.Esmodules, esModulesRelId)
// tagsRelId := &InsertId[string]{id: m.ID, fieldName: "module_id", tableName: "tags"}
// InsertSimpleSliceParallel(group, tx, m.Tags, tagsRelId)
scriptRelId := &InsertId[string]{id: m.ID, fieldName: "module_id", tableName: "scripts"}
InsertSimpleSliceParallel(group, tx, m.Scripts, scriptRelId)
esModulesRelId := &InsertId[string]{id: m.ID, fieldName: "module_id", tableName: "es_modules"}
InsertSimpleSliceParallel(group, tx, m.Esmodules, esModulesRelId)
tagsRelId := &InsertId[string]{id: m.ID, fieldName: "module_id", tableName: "tags"}
InsertSimpleSliceParallel(group, tx, m.Tags, tagsRelId)
// InsertSliceParallel(group, tx, m.Authors, relId)
// InsertSliceParallel(group, tx, m.Media, relId)
// InsertSliceParallel(group, tx, m.Styles, relId)
// InsertSliceParallel(group, tx, m.Languages, relId)
// InsertSliceParallel(group, tx, m.Packs, relId)
// InsertSliceParallel(group, tx, m.PackFolders, relId)
InsertSliceParallel(group, tx, m.Authors, relId)
InsertSliceParallel(group, tx, m.Media, relId)
InsertSliceParallel(group, tx, m.Styles, relId)
InsertSliceParallel(group, tx, m.Languages, relId)
InsertSliceParallel(group, tx, m.Packs, relId)
InsertSliceParallel(group, tx, m.PackFolders, relId)
err := group.Wait()
if err != nil && !errors.Is(err, sql.ErrNoRows) {
@@ -86,21 +93,56 @@ func (m *Module) InsertObjects(tx *sqlx.Tx) error {
return nil
}
func (m *Module) ConnectGame(tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, m.ID}
_, err := tx.Exec(data.query, args...)
return err
}
func (m *Module) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, m.ID, m.Title, m.Description, m.URL, m.License, m.Readme, m.Bugs,
setupID := sql.NullInt64{Int64: int64(data.id), Valid: strings.EqualFold(data.fieldName, "setup_id")}
args := []any{setupID, data.id, m.ID, m.Title, m.Description, m.URL, m.License, m.Readme, m.Bugs,
m.Changelog, m.Version, m.Manifest, m.Download, m.Socket, m.Protected, m.Exclusive, m.PersistentStorage,
m.CoreTranslation, m.Library, m.Locked, m.Owned, m.HasStorage, m.Active, m.Availability}
_, err := tx.Exec(data.query, args...)
res, err := tx.Exec(data.query, args...)
if err != nil {
return err
}
return m.InsertObjects(tx)
rowsAffected, err := res.RowsAffected()
if rowsAffected != 0 {
err = m.InsertObjects(tx)
if err != nil {
return err
}
}
dataCopy := *data
m.ConnectGameQuery(&dataCopy)
return m.ConnectGame(tx, &dataCopy)
}
func (m *Module) ConnectGameCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, m.ID}
_, err := tx.ExecContext(ctx, data.query, args...)
return err
}
func (m *Module) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
@@ -108,14 +150,27 @@ func (m *Module) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint
return ErrNoQuery
}
args := []any{data.id, m.ID, m.Title, m.Description, m.URL, m.License, m.Readme, m.Bugs,
setupID := sql.NullInt64{Int64: int64(data.id), Valid: strings.EqualFold(data.fieldName, "setup_id")}
args := []any{setupID, data.id, m.ID, m.Title, m.Description, m.URL, m.License, m.Readme, m.Bugs,
m.Changelog, m.Version, m.Manifest, m.Download, m.Socket, m.Protected, m.Exclusive, m.PersistentStorage,
m.CoreTranslation, m.Library, m.Locked, m.Owned, m.HasStorage, m.Active, m.Availability}
_, err := tx.ExecContext(ctx, data.query, args...)
res, err := tx.ExecContext(ctx, data.query, args...)
if err != nil {
return err
}
return m.InsertObjects(tx)
rowsAffected, err := res.RowsAffected()
if rowsAffected != 0 {
err = m.InsertObjects(tx)
if err != nil {
return err
}
}
dataCopy := *data
m.ConnectGameQuery(&dataCopy)
return m.ConnectGameCtx(ctx, tx, &dataCopy)
}

View File

@@ -16,7 +16,7 @@ type GameOptions struct {
func (g *GameOptions) Query(data *InsertId[uint]) {
data.query = `
INSERT INTO featured_content (game_id, language, update_channel, port)
INSERT INTO game_options (game_id, language, update_channel, port)
VALUES ($1, $2, $3, $4)
RETURNING id`
}

View File

@@ -5,16 +5,14 @@ import (
"database/sql"
"errors"
"fmt"
"strconv"
"github.com/jmoiron/sqlx"
"golang.org/x/sync/errgroup"
)
type Pack struct {
ID uint
ID string
Key string
Name string
Label string
Banner string
@@ -30,18 +28,17 @@ type Pack struct {
func (p *Pack) Query(data *InsertId[string]) {
data.query = fmt.Sprintf(`
INSERT INTO pack (%s, key_, name, label, banner, path, type, system, package_type, package_name)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
RETURNING id`, data.fieldName)
INSERT INTO pack (%s, id, name, label, banner, path, type, system, package_type, package_name)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)`, data.fieldName)
}
func (p *Pack) InsertObjects(tx *sqlx.Tx) error {
group, ctx := errgroup.WithContext(context.Background())
InsertWithCtxParallel(group, ctx, tx, p.Ownership,
InsertId[string]{id: strconv.FormatUint(uint64(p.ID), 10), fieldName: "pack_id"})
InsertId[string]{id: p.ID, fieldName: "pack_id"})
relId := InsertId[uint]{id: p.ID, fieldName: "pack_id"}
relId := InsertId[string]{id: p.ID, fieldName: "pack_id"}
InsertSliceParallel(group, tx, p.Index, relId)
InsertSliceParallel(group, tx, p.Folders, relId)
@@ -57,9 +54,10 @@ func (p *Pack) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
return ErrNoQuery
}
args := []any{data.id, p.Key, p.Name, p.Label, p.Banner, p.Path, p.Type, p.System, p.PackageType, p.PackageName}
args := []any{data.id, p.ID, p.Name, p.Label, p.Banner, p.Path, p.Type,
p.System, p.PackageType, p.PackageName}
err := tx.QueryRowx(data.query, args...).Scan(&p.ID)
_, err := tx.Exec(data.query, args...)
if err != nil {
return err
}
@@ -72,9 +70,10 @@ func (p *Pack) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string
return ErrNoQuery
}
args := []any{data.id, p.Key, p.Name, p.Label, p.Banner, p.Path, p.Type, p.System, p.PackageType, p.PackageName}
args := []any{data.id, p.ID, p.Name, p.Label, p.Banner, p.Path, p.Type,
p.System, p.PackageType, p.PackageName}
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&p.ID)
_, err := tx.ExecContext(ctx, data.query, args...)
if err != nil {
return err
}
@@ -92,13 +91,13 @@ type PackFolder struct {
Sort int
}
func (p *PackFolder) Query(data *InsertId[uint]) {
func (p *PackFolder) Query(data *InsertId[string]) {
data.query = fmt.Sprintf(`
INSERT INTO pack_folder (%s, id, description, name, sorting, type, sort)
VALUES ($1, $2, $3, $4, $5, $6, $7)`, data.fieldName)
}
func (p *PackFolder) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
func (p *PackFolder) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}
@@ -113,7 +112,7 @@ func (p *PackFolder) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
return nil
}
func (p *PackFolder) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
func (p *PackFolder) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}

View File

@@ -83,8 +83,26 @@ func (p *PackageWarningsData) InsertObjects(tx *sqlx.Tx) error {
func (p *PackageWarningsData) Query(data *InsertId[uint]) {
data.query = `
INSERT INTO package_warnings_data (package_warnings_id, id, type, reinstallable, manifest)
VALUES ($1, $2, $3, $4, $5)`
INSERT INTO package_warnings_data (id, type, reinstallable, manifest)
VALUES ($1, $2, $3, $4)
ON CONFLICT(id) DO NOTHING`
}
func (p *PackageWarningsData) ConnectGameQuery(data *InsertId[uint]) {
data.query = `
INSERT INTO package_warnings_to_data (package_warnings_id, package_warnings_data_id)
VALUES ($1, $2)`
}
func (p *PackageWarningsData) ConnectGame(tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, p.ID}
_, err := tx.Exec(data.query, args...)
return err
}
func (p *PackageWarningsData) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
@@ -92,14 +110,36 @@ func (p *PackageWarningsData) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
return ErrNoQuery
}
args := []any{data.id, p.ID, p.Type, p.Reinstallable, p.Manifest}
args := []any{p.ID, p.Type, p.Reinstallable, p.Manifest}
_, err := tx.Exec(data.query, args...)
res, err := tx.Exec(data.query, args...)
if err != nil {
return err
}
return p.InsertObjects(tx)
rowsAffected, err := res.RowsAffected()
if rowsAffected != 0 {
err = p.InsertObjects(tx)
}
if err != nil {
return err
}
dataCopy := *data
p.ConnectGameQuery(&dataCopy)
return p.ConnectGame(tx, &dataCopy)
}
func (p *PackageWarningsData) ConnectGameCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, p.ID}
_, err := tx.ExecContext(ctx, data.query, args...)
return err
}
func (p *PackageWarningsData) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
@@ -107,12 +147,23 @@ func (p *PackageWarningsData) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *
return ErrNoQuery
}
args := []any{data.id, p.ID, p.Type, p.Reinstallable, p.Manifest}
args := []any{p.ID, p.Type, p.Reinstallable, p.Manifest}
_, err := tx.ExecContext(ctx, data.query, args...)
res, err := tx.ExecContext(ctx, data.query, args...)
if err != nil {
return err
}
return p.InsertObjects(tx)
rowsAffected, err := res.RowsAffected()
if rowsAffected != 0 {
err = p.InsertObjects(tx)
}
if err != nil {
return err
}
dataCopy := *data
p.ConnectGameQuery(&dataCopy)
return p.ConnectGameCtx(ctx, tx, &dataCopy)
}

View File

@@ -1,5 +1,15 @@
package db
import (
"context"
"database/sql"
"errors"
"fmt"
"github.com/jmoiron/sqlx"
"golang.org/x/sync/errgroup"
)
type Ring struct {
ID uint
@@ -9,6 +19,57 @@ type Ring struct {
Subject Subject
}
func (r *Ring) Query(data *InsertId[uint]) {
data.query = fmt.Sprintf(`
INSERT INTO ring (%s, enabled, effects)
VALUES ($1, $2, $3)
RETURNING id`, data.fieldName)
}
func (r *Ring) InsertObjects(tx *sqlx.Tx) error {
group, ctx := errgroup.WithContext(context.Background())
relId := InsertId[uint]{id: r.ID, fieldName: "ring_id"}
InsertWithCtxParallel(group, ctx, tx, r.RingColors, relId)
InsertWithCtxParallel(group, ctx, tx, r.Subject, relId)
err := group.Wait()
if err != nil && !errors.Is(err, sql.ErrNoRows) {
return err
}
return nil
}
func (r *Ring) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, r.Enabled, r.Effects}
err := tx.QueryRowx(data.query, args...).Scan(&r.ID)
if err != nil {
return err
}
return nil
}
func (r *Ring) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, r.Enabled, r.Effects}
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&r.ID)
if err != nil {
return err
}
return nil
}
type RingColors struct {
ID uint
@@ -16,9 +77,83 @@ type RingColors struct {
Background string
}
func (r RingColors) Query(data *InsertId[uint]) {
data.query = fmt.Sprintf(`
INSERT INTO ring_colors (%s, ring, background)
VALUES ($1, $2, $3)
RETURNING id`, data.fieldName)
}
func (r RingColors) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, r.Ring, r.Background}
err := tx.QueryRowx(data.query, args...).Scan(&r.ID)
if err != nil {
return err
}
return nil
}
func (r RingColors) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, r.Ring, r.Background}
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&r.ID)
if err != nil {
return err
}
return nil
}
type Subject struct {
ID uint
Scale int
Texture string
}
func (s Subject) Query(data *InsertId[uint]) {
data.query = fmt.Sprintf(`
INSERT INTO ring_colors (%s, scale, texture)
VALUES ($1, $2, $3)
RETURNING id`, data.fieldName)
}
func (s Subject) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, s.Scale, s.Texture}
err := tx.QueryRowx(data.query, args...).Scan(&s.ID)
if err != nil {
return err
}
return nil
}
func (s Subject) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, s.Scale, s.Texture}
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&s.ID)
if err != nil {
return err
}
return nil
}

View File

@@ -21,7 +21,7 @@ type Scene struct {
Grid SceneGrid
TokenVision bool
Drawings []SceneDrawing
Tokens []Token
Tokens []*Token
Lights []SceneLight
Notes []Note
Sounds []ScenesSound
@@ -119,7 +119,7 @@ type SceneLight struct {
Rotation int
Walls bool
Vision bool
Config Light
Config *Light
Hidden bool
Elevation int
}

View File

@@ -4,7 +4,7 @@ import (
"context"
"database/sql"
"errors"
"fmt"
"strings"
"github.com/jmoiron/sqlx"
"golang.org/x/sync/errgroup"
@@ -48,12 +48,18 @@ type System struct {
}
func (s *System) Query(data *InsertId[string]) {
data.query = fmt.Sprintf(`
INSERT INTO system (%s, id, title, description, url, license, bugs, changelog, version, manifest,
data.query = `
INSERT INTO system (setup_id, id, title, description, url, license, bugs, changelog, version, manifest,
download, background, primary_token_attribute, availability, socket, protected, exclusive_,
persistent_storage, locked, owned, has_storage)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21)`,
data.fieldName)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21)
ON CONFLICT(id) DO NOTHING`
}
func (s *System) ConnectGameQuery(data *InsertId[string]) {
data.query = `
INSERT INTO game_to_systems (game_id, system_id)
VALUES ($1, $2)`
}
func (s *System) InsertObjects(tx *sqlx.Tx) error {
@@ -86,22 +92,57 @@ func (s *System) InsertObjects(tx *sqlx.Tx) error {
return nil
}
func (s *System) ConnectGame(tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, s.ID}
_, err := tx.Exec(data.query, args...)
return err
}
func (s *System) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, s.ID, s.Title, s.Description, s.URL, s.License, s.Bugs,
setupID := sql.NullString{String: data.id, Valid: strings.EqualFold(data.fieldName, "setup_id")}
args := []any{setupID, s.ID, s.Title, s.Description, s.URL, s.License, s.Bugs,
s.Changelog, s.Version, s.Manifest, s.Download, s.Background,
s.PrimaryTokenAttribute, s.Availability, s.Socket, s.Protected, s.Exclusive,
s.PersistentStorage, s.Locked, s.Owned, s.HasStorage}
_, err := tx.Exec(data.query, args...)
res, err := tx.Exec(data.query, args...)
if err != nil {
return err
}
return s.InsertObjects(tx)
rowsAffected, err := res.RowsAffected()
if rowsAffected != 0 {
err = s.InsertObjects(tx)
if err != nil {
return err
}
}
dataCopy := *data
s.ConnectGameQuery(&dataCopy)
return s.ConnectGame(tx, &dataCopy)
}
func (s *System) ConnectGameCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, s.ID}
_, err := tx.ExecContext(ctx, data.query, args...)
return err
}
func (s *System) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
@@ -109,15 +150,28 @@ func (s *System) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[stri
return ErrNoQuery
}
args := []any{data.id, s.ID, s.Title, s.Description, s.URL, s.License, s.Bugs,
setupID := sql.NullString{String: data.id, Valid: strings.EqualFold(data.fieldName, "setup_id")}
args := []any{setupID, s.ID, s.Title, s.Description, s.URL, s.License, s.Bugs,
s.Changelog, s.Version, s.Manifest, s.Download, s.Background,
s.PrimaryTokenAttribute, s.Availability, s.Socket, s.Protected, s.Exclusive,
s.PersistentStorage, s.Locked, s.Owned, s.HasStorage}
_, err := tx.ExecContext(ctx, data.query, args...)
res, err := tx.ExecContext(ctx, data.query, args...)
if err != nil {
return err
}
return s.InsertObjects(tx)
rowsAffected, err := res.RowsAffected()
if rowsAffected != 0 {
err = s.InsertObjects(tx)
if err != nil {
return err
}
}
dataCopy := *data
s.ConnectGameQuery(&dataCopy)
return s.ConnectGameCtx(ctx, tx, &dataCopy)
}

View File

@@ -1,5 +1,15 @@
package db
import (
"context"
"database/sql"
"errors"
"fmt"
"github.com/jmoiron/sqlx"
"golang.org/x/sync/errgroup"
)
type Token struct {
ID uint
@@ -16,16 +26,76 @@ type Token struct {
Alpha int
Width float64
Height float64
Ring Ring
Sight TokenSight
Texture TokenTexture
Ring *Ring
Sight *TokenSight
Texture *TokenTexture
Bar1 TokenBar
Bar2 TokenBar
Light Light
Light *Light
Occludable TokenOccludable
TurnMarker TokenTurnMarker
}
func (t *Token) Query(data *InsertId[string]) {
data.query = fmt.Sprintf(`
INSERT INTO token (%s, name, actor_link_ append_number, prepend_adjective, lock_rotation, random_img, display_name,
display_bars, disposition, rotation, alpha, width, height)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)
RETURNING id`, data.fieldName)
}
func (t *Token) InsertObjects(tx *sqlx.Tx) error {
group, ctx := errgroup.WithContext(context.Background())
relId := InsertId[uint]{id: t.ID, fieldName: "token_id"}
InsertWithCtxParallel(group, ctx, tx, t.Ring, relId)
InsertWithCtxParallel(group, ctx, tx, t.Sight, relId)
InsertWithCtxParallel(group, ctx, tx, t.Texture, relId)
InsertWithCtxParallel(group, ctx, tx, t.Bar1, InsertId[uint]{id: t.ID, fieldName: "token_id", tableName: "token_bar_1"})
InsertWithCtxParallel(group, ctx, tx, t.Bar1, InsertId[uint]{id: t.ID, fieldName: "token_id", tableName: "token_bar_2"})
InsertWithCtxParallel(group, ctx, tx, t.Light, relId)
InsertWithCtxParallel(group, ctx, tx, t.Occludable, relId)
InsertWithCtxParallel(group, ctx, tx, t.TurnMarker, relId)
err := group.Wait()
if err != nil && !errors.Is(err, sql.ErrNoRows) {
return err
}
return nil
}
func (t *Token) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, t.Name, t.ActorLink, t.AppendNumber, t.PrependAdjective, t.LockRotation, t.RandomImg,
t.DisplayName, t.DisplayBars, t.Disposition, t.Rotation, t.Alpha, t.Width, t.Height}
err := tx.QueryRowx(data.query, args...).Scan(&t.ID)
if err != nil {
return err
}
return t.InsertObjects(tx)
}
func (t *Token) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, t.Name, t.ActorLink, t.AppendNumber, t.PrependAdjective, t.LockRotation, t.RandomImg,
t.DisplayName, t.DisplayBars, t.Disposition, t.Rotation, t.Alpha, t.Width, t.Height}
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&t.ID)
if err != nil {
return err
}
return t.InsertObjects(tx)
}
type TokenTexture struct {
ID uint
@@ -42,6 +112,43 @@ type TokenTexture struct {
AlphaThreshold float64
}
func (t *TokenTexture) Query(data *InsertId[uint]) {
data.query = fmt.Sprintf(`
INSERT INTO token_texture (%s, src, fit, tint, scale_x, scale_y, offset_x, offset_y, rotation, anchor_x, anchor_y, alpha_threshold)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
RETURNING id`, data.fieldName)
}
func (t *TokenTexture) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, t.Src, t.Fit, t.Tint, t.ScaleX, t.ScaleY, t.OffsetX, t.OffsetY, t.Rotation, t.AnchorX, t.AnchorY, t.AlphaThreshold}
err := tx.QueryRowx(data.query, args...).Scan(&t.ID)
if err != nil {
return err
}
return nil
}
func (t *TokenTexture) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, t.Src, t.Fit, t.Tint, t.ScaleX, t.ScaleY, t.OffsetX, t.OffsetY, t.Rotation, t.AnchorX, t.AnchorY, t.AlphaThreshold}
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&t.ID)
if err != nil {
return err
}
return nil
}
type TokenSight struct {
ID uint
@@ -54,18 +161,129 @@ type TokenSight struct {
Enabled bool
}
func (t *TokenSight) Query(data *InsertId[uint]) {
data.query = fmt.Sprintf(`
INSERT INTO token_sight (%s, color, vision_mode, range_, angle, attenuation, brightness, enabled)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
RETURNING id`, data.fieldName)
}
func (t *TokenSight) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, t.Color, t.VisionMode, t.Range, t.Angle, t.Attenuation, t.Brightness, t.Enabled}
err := tx.QueryRowx(data.query, args...).Scan(&t.ID)
if err != nil {
return err
}
return nil
}
func (t *TokenSight) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, t.Color, t.VisionMode, t.Range, t.Angle, t.Attenuation, t.Brightness, t.Enabled}
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&t.ID)
if err != nil {
return err
}
return nil
}
type TokenBar struct {
ID uint
Attribute string
}
func (t TokenBar) Query(data *InsertId[uint]) {
data.query = fmt.Sprintf(`
INSERT INTO %s (%s, attribute)
VALUES ($1, $2)
RETURNING id`, data.tableName, data.fieldName)
}
func (t TokenBar) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, t.Attribute}
err := tx.QueryRowx(data.query, args...).Scan(&t.ID)
if err != nil {
return err
}
return nil
}
func (t TokenBar) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, t.Attribute}
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&t.ID)
if err != nil {
return err
}
return nil
}
type TokenOccludable struct {
ID uint
Radius int
}
func (t TokenOccludable) Query(data *InsertId[uint]) {
data.query = fmt.Sprintf(`
INSERT INTO token_occludable (%s, radius)
VALUES ($1, $2)
RETURNING id`, data.fieldName)
}
func (t TokenOccludable) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, t.Radius}
err := tx.QueryRowx(data.query, args...).Scan(&t.ID)
if err != nil {
return err
}
return nil
}
func (t TokenOccludable) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, t.Radius}
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&t.ID)
if err != nil {
return err
}
return nil
}
type TokenTurnMarker struct {
ID uint
@@ -74,3 +292,40 @@ type TokenTurnMarker struct {
Src string
Disposition bool
}
func (t TokenTurnMarker) Query(data *InsertId[uint]) {
data.query = fmt.Sprintf(`
INSERT INTO token_turn_maker (%s, mode, animation, src, disposition)
VALUES ($1, $2, $3, $4, $5)
RETURNING id`, data.fieldName)
}
func (t TokenTurnMarker) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, t.Mode, t.Animation, t.Src, t.Disposition}
err := tx.QueryRowx(data.query, args...).Scan(&t.ID)
if err != nil {
return err
}
return nil
}
func (t TokenTurnMarker) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, t.Mode, t.Animation, t.Src, t.Disposition}
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&t.ID)
if err != nil {
return err
}
return nil
}

View File

@@ -7,6 +7,7 @@ import (
"time"
"github.com/jmoiron/sqlx"
"github.com/mattn/go-sqlite3"
"golang.org/x/sync/errgroup"
)
@@ -172,3 +173,14 @@ func DeleteAllSeq(db *sqlx.DB) error {
return nil
}
func IsErrUniqueConstraint(err error) {
if sqliteErr, ok := err.(sqlite3.Error); ok {
// SQLITE_CONSTRAINT_UNIQUE (extended code 2067)
// or SQLITE_CONSTRAINT (basic code 19)
if sqliteErr.ExtendedCode == sqlite3.ErrConstraintUnique ||
sqliteErr.Code == sqlite3.ErrConstraint {
fmt.Println("Record already exists")
}
}
}

View File

@@ -48,10 +48,14 @@ type World struct {
func (w *World) Query(data *InsertId[string]) {
data.query = fmt.Sprintf(`
INSERT INTO world (%s, id, title, description, version, system, background, join_theme,
INSERT INTO world (%[1]s, id, title, description, version, system, background, join_theme,
core_version, system_version, last_played, playtime, availability, next_session, socket,
protected, exclusive_, persistent_storage, locked, owned, has_storage)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21)`,
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21)
ON CONFLICT(id) DO UPDATE SET
%[1]s = EXCLUDED.%[1]s,
updated_at = datetime('now')
RETURNING (created_at == updated_at) AS was_inserted`,
data.fieldName)
}
@@ -92,12 +96,16 @@ func (w *World) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
w.CoreVersion, w.SystemVersion, w.LastPlayed, w.Playtime, w.Availability, w.NextSession, w.Socket,
w.Protected, w.Exclusive, w.PersistentStorage, w.Locked, w.Owned, w.HasStorage}
_, err := tx.Exec(data.query, args...)
var isInserted bool
err := tx.QueryRowx(data.query, args...).Scan(&isInserted)
if err != nil {
return err
}
return w.InsertObjects(tx)
if isInserted {
return w.InsertObjects(tx)
}
return nil
}
func (w *World) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
@@ -109,10 +117,14 @@ func (w *World) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[strin
w.CoreVersion, w.SystemVersion, w.LastPlayed, w.Playtime, w.Availability, w.NextSession, w.Socket,
w.Protected, w.Exclusive, w.PersistentStorage, w.Locked, w.Owned, w.HasStorage}
_, err := tx.ExecContext(ctx, data.query, args...)
var isInserted bool
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&isInserted)
if err != nil {
return err
}
return w.InsertObjects(tx)
if isInserted {
return w.InsertObjects(tx)
}
return nil
}

View File

@@ -18,24 +18,28 @@ type Actor struct {
// ActorsEffects []any `json:"effects"`
}
func (a *Actor) ToDB(dest *db.Actor) bool {
func (a *Actor) ToDB(dest **db.Actor) bool {
if dest == nil {
return false
}
dest.Img = a.Img
dest.Name = a.Name
dest.Type = a.Type
dest.Folder = a.Folder
dest.Sort = a.Sort
dest.ID = a.ID
actor := &db.Actor{
Img: a.Img,
Name: a.Name,
Type: a.Type,
Folder: a.Folder,
Sort: a.Sort,
ID: a.ID,
}
a.PrototypeToken.ToDB(&dest.PrototypeToken)
a.Stats.ToDB(&dest.Stats)
a.PrototypeToken.ToDB(&actor.PrototypeToken)
a.Stats.ToDB(&actor.Stats)
OwnershipToDB(&dest.Ownership, a.Ownership)
OwnershipToDB(&actor.Ownership, a.Ownership)
CopySliceToDB(&dest.Items, a.Items)
CopySliceToDB(&actor.Items, a.Items)
*dest = actor
return true
}

View File

@@ -3,20 +3,20 @@ package json
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/temp_models/db"
type CardDeck struct {
Name string `json:"name"`
Type string `json:"type"`
Description string `json:"description"`
Img string `json:"img"`
Cards []*Card `json:"cards"`
Width int `json:"width"`
Height int `json:"height"`
Rotation int `json:"rotation"`
DisplayCount bool `json:"displayCount"`
Stats Stats `json:"_stats"`
Ownership Ownership `json:"ownership"`
Folder string `json:"folder"`
Sort int `json:"sort"`
ID string `json:"_id"`
Name string `json:"name"`
Type string `json:"type"`
Description string `json:"description"`
Img string `json:"img"`
Cards []*Card `json:"cards"`
Width int `json:"width"`
Height int `json:"height"`
Rotation int `json:"rotation"`
DisplayCount bool `json:"displayCount"`
Stats Stats `json:"_stats"`
Ownership map[string]int `json:"ownership,omitempty"`
Folder string `json:"folder"`
Sort int `json:"sort"`
ID string `json:"_id"`
// Flags any `json:"flags"`
// Cards0System any `json:"system"`
}
@@ -41,8 +41,8 @@ func (c *CardDeck) ToDB(dest **db.CardDeck) bool {
}
c.Stats.ToDB(&cardDeck.Stats)
c.Ownership.ToDB(&cardDeck.Ownership)
OwnershipToDB(&cardDeck.Ownership, c.Ownership)
CopySliceToDB(&cardDeck.Cards, c.Cards)
*dest = cardDeck

View File

@@ -1,6 +1,7 @@
package json
import (
"encoding/json"
"sync"
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/temp_models/db"
@@ -40,6 +41,19 @@ type Game struct {
// Template Template `json:"template"`
}
func ParseGame(data []byte) (*Game, error) {
var modelGame []Game
err := json.Unmarshal(data, &modelGame)
if err != nil {
return nil, err
}
if len(modelGame) > 1 {
return nil, ErrorSetupMoreThanOne
}
return &modelGame[0], nil
}
func (g *Game) ToDB(dest *db.Game) bool {
dest.UserID = g.UserID
dest.DemoMode = g.DemoMode

View File

@@ -39,6 +39,8 @@ func (j *JournalPage) ToDB(dest **db.JournalPage) bool {
OwnershipToDB(&journalPage.Ownership, j.Ownership)
*dest = journalPage
return true
}

View File

@@ -20,27 +20,31 @@ type Light struct {
Color string `json:"color"`
}
func (l *Light) ToDB(dest *db.Light) bool {
func (l *Light) ToDB(dest **db.Light) bool {
if dest == nil {
return false
}
dest.Alpha = l.Alpha
dest.Angle = l.Angle
dest.Bright = l.Bright
dest.Coloration = l.Coloration
dest.Dim = l.Dim
dest.Attenuation = l.Attenuation
dest.Luminosity = l.Luminosity
dest.Saturation = l.Saturation
dest.Contrast = l.Contrast
dest.Shadows = l.Shadows
dest.Negative = l.Negative
dest.Priority = l.Priority
dest.Color = l.Color
light := &db.Light{
Alpha: l.Alpha,
Angle: l.Angle,
Bright: l.Bright,
Coloration: l.Coloration,
Dim: l.Dim,
Attenuation: l.Attenuation,
Luminosity: l.Luminosity,
Saturation: l.Saturation,
Contrast: l.Contrast,
Shadows: l.Shadows,
Negative: l.Negative,
Priority: l.Priority,
Color: l.Color,
}
l.LightAnimation.ToDB(&dest.LightAnimation)
l.LightDarkness.ToDB(&dest.LightDarkness)
l.LightAnimation.ToDB(&light.LightAnimation)
l.LightDarkness.ToDB(&light.LightDarkness)
*dest = light
return true
}

View File

@@ -36,7 +36,7 @@ func (p *Pack) ToDB(dest **db.Pack) bool {
System: p.System,
PackageType: p.PackageType,
PackageName: p.PackageName,
Key: p.Id,
ID: p.Id,
}
p.Ownership.ToDB(&pack.Ownership)

View File

@@ -45,6 +45,8 @@ func (p *Playlist) ToDB(dest **db.Playlist) bool {
CopySliceToDB(&playlist.Sounds, p.Sounds)
*dest = playlist
return true
}

View File

@@ -9,15 +9,20 @@ type Ring struct {
Subject Subject `json:"subject"`
}
func (r *Ring) ToDB(dest *db.Ring) bool {
func (r *Ring) ToDB(dest **db.Ring) bool {
if dest == nil {
return false
}
dest.Enabled = r.Enabled
dest.Effects = r.Effects
r.RingColors.ToDB(&dest.RingColors)
r.Subject.ToDB(&dest.Subject)
ring := &db.Ring{
Enabled: r.Enabled,
Effects: r.Effects,
}
r.RingColors.ToDB(&ring.RingColors)
r.Subject.ToDB(&ring.Subject)
*dest = ring
return true
}

View File

@@ -41,6 +41,8 @@ func (t *Table) ToDB(dest **db.Table) bool {
CopySliceToDB(&table.Results, t.Results)
*dest = table
return true
}

View File

@@ -29,33 +29,37 @@ type Token struct {
// DetectionModes []any `json:"detectionModes"`
}
func (t *Token) ToDB(dest *db.Token) bool {
func (t *Token) ToDB(dest **db.Token) bool {
if dest == nil {
return false
}
dest.DisplayName = t.DisplayName
dest.DisplayBars = t.DisplayBars
dest.Disposition = t.Disposition
dest.Name = t.Name
dest.ActorLink = t.ActorLink
dest.AppendNumber = t.AppendNumber
dest.PrependAdjective = t.PrependAdjective
dest.Width = t.Width
dest.Height = t.Height
dest.LockRotation = t.LockRotation
dest.Rotation = t.Rotation
dest.Alpha = t.Alpha
dest.RandomImg = t.RandomImg
token := &db.Token{
DisplayName: t.DisplayName,
DisplayBars: t.DisplayBars,
Disposition: t.Disposition,
Name: t.Name,
ActorLink: t.ActorLink,
AppendNumber: t.AppendNumber,
PrependAdjective: t.PrependAdjective,
Width: t.Width,
Height: t.Height,
LockRotation: t.LockRotation,
Rotation: t.Rotation,
Alpha: t.Alpha,
RandomImg: t.RandomImg,
}
t.Ring.ToDB(&dest.Ring)
t.Sight.ToDB(&dest.Sight)
t.Texture.ToDB(&dest.Texture)
t.Bar1.ToDB(&dest.Bar1)
t.Bar2.ToDB(&dest.Bar2)
t.Light.ToDB(&dest.Light)
t.Occludable.ToDB(&dest.Occludable)
t.TurnMarker.ToDB(&dest.TurnMarker)
t.Ring.ToDB(&token.Ring)
t.Sight.ToDB(&token.Sight)
t.Texture.ToDB(&token.Texture)
t.Bar1.ToDB(&token.Bar1)
t.Bar2.ToDB(&token.Bar2)
t.Light.ToDB(&token.Light)
t.Occludable.ToDB(&token.Occludable)
t.TurnMarker.ToDB(&token.TurnMarker)
*dest = token
return true
}
@@ -74,22 +78,26 @@ type TokenTexture struct {
AlphaThreshold float64 `json:"alphaThreshold"`
}
func (t *TokenTexture) ToDB(dest *db.TokenTexture) bool {
func (t *TokenTexture) ToDB(dest **db.TokenTexture) bool {
if dest == nil {
return false
}
dest.Src = t.Src
dest.ScaleX = t.ScaleX
dest.ScaleY = t.ScaleY
dest.OffsetX = t.OffsetX
dest.OffsetY = t.OffsetY
dest.Rotation = t.Rotation
dest.AnchorX = t.AnchorX
dest.AnchorY = t.AnchorY
dest.Fit = t.Fit
dest.Tint = t.Tint
dest.AlphaThreshold = t.AlphaThreshold
texture := &db.TokenTexture{
Src: t.Src,
ScaleX: t.ScaleX,
ScaleY: t.ScaleY,
OffsetX: t.OffsetX,
OffsetY: t.OffsetY,
Rotation: t.Rotation,
AnchorX: t.AnchorX,
AnchorY: t.AnchorY,
Fit: t.Fit,
Tint: t.Tint,
AlphaThreshold: t.AlphaThreshold,
}
*dest = texture
return true
}
@@ -104,18 +112,22 @@ type TokenSight struct {
Brightness float64
}
func (t *TokenSight) ToDB(dest *db.TokenSight) bool {
func (t *TokenSight) ToDB(dest **db.TokenSight) bool {
if dest == nil {
return false
}
dest.Color = t.Color
dest.Enabled = t.Enabled
dest.Range = t.Range
dest.Angle = t.Angle
dest.VisionMode = t.VisionMode
dest.Attenuation = t.Attenuation
dest.Brightness = t.Brightness
sight := &db.TokenSight{
Color: t.Color,
Enabled: t.Enabled,
Range: t.Range,
Angle: t.Angle,
VisionMode: t.VisionMode,
Attenuation: t.Attenuation,
Brightness: t.Brightness,
}
*dest = sight
return true
}

View File

@@ -20,33 +20,22 @@ func (tr *FoundryTransport) FillDBWithFoundryData() {
// return
// }
// err = tr.InitWorldsData(idState)
// if err != nil {
// tr.ReadChan.Err() <- &types.FoundryError{Direction: types.WriterCode, Err: err, Type: types.DbCode}
// return
// }
err = tr.InitWorldsData()
if err != nil {
tr.ReadChan.Err() <- &types.FoundryError{Direction: types.WriterCode, Err: err, Type: types.DbCode}
return
}
}
func (tr *FoundryTransport) InitSetupData() error {
db.DeleteAll(tr.DB)
db.DeleteAllSeq(tr.DB)
for k := range requests.PathToSetupState {
err := tr.InsertJsonDataToDB(k)
if err != nil {
return err
}
}
return nil
return tr.InsertSetupToDB()
}
func (tr *FoundryTransport) InsertJsonDataToDB(statePath string) error {
_, ok1 := requests.PathToSetupState[statePath]
_, ok2 := requests.PathToWorldState[statePath]
if !ok1 && !ok2 {
return ErrorStateTypeNotExist
}
msgJson, err := tr.GetJsonData(statePath)
func (tr *FoundryTransport) InsertSetupToDB() error {
msgJson, err := tr.GetJsonDataByType(requests.SetupPath)
if err != nil {
return err
}
@@ -67,35 +56,49 @@ func (tr *FoundryTransport) InsertJsonDataToDB(statePath string) error {
return nil
}
// func (tr *FoundryTransport) InitWorldsData(idState int64) error {
// worlds, err := tr.Models.FoundryState.GetWorlds(idState)
// if err != nil {
// return err
// }
// tr.Logger.Debug("Worlds", "len", len(worlds))
func (tr *FoundryTransport) InitWorldsData() error {
worlds := []string{"kingmaker"} //TODO: get world name from database
// for i := range worlds {
// worldName := worlds[i].TextId
// tr.InitWorldData(worldName)
// _, err = tr.Http.PostReturnToSetup()
// if err != nil {
// return err
// }
// }
// return nil
// }
for i := range worlds {
worldName := worlds[i]
tr.InitWorldData(worldName)
_, err := tr.Http.PostReturnToSetup()
if err != nil {
return err
}
}
return nil
}
// func (tr *FoundryTransport) InitWorldData(worldName string) error {
// tr.Logger.Debug("World name", "name", worldName)
// err := tr.LaunchWorld(worldName)
// if err != nil {
// return err
// }
// for k := range requests.PathToWorldState {
// err := tr.InsertJsonDataToDB(k)
// if err != nil {
// return err
// }
// }
// return nil
// }
func (tr *FoundryTransport) InitWorldData(worldName string) error {
tr.Logger.Debug("World name", "name", worldName)
err := tr.LaunchWorld(worldName)
if err != nil {
return err
}
return tr.InsertGameToDB()
}
func (tr *FoundryTransport) InsertGameToDB() error {
err := tr.ConnectToWorld()
if err != nil {
return err
}
msgJson, err := tr.GetJsonData("world")
if err != nil {
return err
}
tr.Logger.Info("Game data received")
gameJson, err := json.ParseGame(msgJson)
if err != nil {
return err
}
var foundryStateDb db.Game
gameJson.ToDB(&foundryStateDb)
return foundryStateDb.Insert(tr.DB)
}

View File

@@ -3,9 +3,11 @@ package transport
import "errors"
var (
ErrorNotAuth = errors.New("Admin is not authenticated")
ErrorIsNotReady = errors.New("Connection is not ready for communication")
ErrorTimeout = errors.New("Answer has not been received after timeout")
ErrorStateTypeNotExist = errors.New("State type does not exists")
ErrorAuthPassWrong = errors.New("Authentication password is wrong")
ErrorNotAuth = errors.New("Admin is not authenticated")
ErrorIsNotReady = errors.New("Connection is not ready for communication")
ErrorTimeout = errors.New("Answer has not been received after timeout")
ErrorStateTypeNotExist = errors.New("State type does not exists")
ErrorAuthPassWrong = errors.New("Authentication password is wrong")
ErrorChannelIsClosed = errors.New("Channel has been closed")
ErrorUserIsNotConnected = errors.New("User is not connected")
)

View File

@@ -17,6 +17,7 @@ type FoundryTransport struct {
ChanMutex sync.Mutex
ReadChan types.ReadChannels
ExchangeChan types.ExchangeChannels
LoggedInChan chan bool
Http *requests.FoundryHttpRequest
ReconnectTimeout time.Duration

View File

@@ -31,13 +31,7 @@ func (tr *FoundryTransport) InitWebSocketConnection() error {
dialer := websocket.DefaultDialer
dialer.ReadBufferSize = 128 * 1024 * 1024
dialer.WriteBufferSize = 128 * 1024 * 1024
// dialer.Jar = tr.Http.Jar
// ur, err := url.Parse(fmt.Sprintf("http://%s", tr.Http.Host))
// if err != nil {
// return err
// }
// fmt.Printf("TEST:%v:%v\n", u.String(), dialer.Jar.Cookies(&u))
wsConn, resp, err := dialer.Dial(u.String(), wsHeader)
if err != nil {
return err
@@ -47,8 +41,6 @@ func (tr *FoundryTransport) InitWebSocketConnection() error {
for i := range cookies {
fmt.Printf("TEST%d: %v\n", i, cookies[i])
}
// fmt.Printf("TEST1: %v\n")
// fmt.Printf("TEST2: %v\n", wsHeader)
tr.WsConn = wsConn
tr.CurrWsId = 0
@@ -118,19 +110,47 @@ func (tr *FoundryTransport) ListenWebSocket() {
}
}
func (tr *FoundryTransport) ConnectToWorld() error {
err := tr.LogInToWorld()
if err != nil {
return err
}
tr.Logger.Info("World is started. Succesfully logged into world")
tr.LoggedInChan = make(chan bool)
close(tr.ReadChan.Reconnect())
val, ok := <-tr.LoggedInChan
if !ok {
return ErrorChannelIsClosed
}
if !val {
return ErrorUserIsNotConnected
}
return nil
}
func (tr *FoundryTransport) SendOnlyCodeRequest(code string) error {
tr.Logger.Debug("WS: Data has been send\n", "msg", types.CodesRespToReq[code])
return tr.WsConn.WriteMessage(websocket.TextMessage, []byte(types.CodesRespToReq[code]))
}
func (tr *FoundryTransport) GetJsonData(stateType string) ([]byte, error) {
func (tr *FoundryTransport) GetJsonDataByType(stateType string) ([]byte, error) {
msg := types.NewWsMessageByPage(stateType, tr.CurrWsId)
tr.CurrWsId++
return tr.HandleWebsocketRequest(msg)
}
func (tr *FoundryTransport) GetJsonData(msg string) ([]byte, error) {
wsMsg := types.NewWsMessage(msg, tr.CurrWsId)
tr.CurrWsId++
return tr.HandleWebsocketRequest(wsMsg)
}
func (tr *FoundryTransport) CloseWebSocketConn() error {
tr.Logger.Debug("Websocket has been closed")
return tr.WsConn.Close()