finish world initialization, set up init on startup and on shutdown, move all models to main directory

This commit is contained in:
lbenedar
2026-04-24 18:04:35 +03:00
parent 07025afefc
commit fd65631be1
125 changed files with 1534 additions and 2802 deletions

View File

@@ -0,0 +1,88 @@
package db
import (
"context"
"database/sql"
"errors"
"github.com/jmoiron/sqlx"
"golang.org/x/sync/errgroup"
)
type Actor struct {
ID string
Img string
Name string
Type string
Folder string
Sort int
PrototypeToken *Token
Stats Stats
Items []*Item
Ownership []OwnershipString
}
func (a *Actor) Query(data *InsertId[uint]) {
data.query = `
INSERT INTO actor (game_id, id, img, name, type, folder, sort)
VALUES ($1, $2, $3, $4, $5, $6, $7)
ON CONFLICT(id) DO UPDATE SET
game_id = EXCLUDED.game_id,
updated_at = datetime('now')
RETURNING (created_at == updated_at) AS is_inserted`
}
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}
var isInserted bool
err := tx.QueryRowx(data.query, args...).Scan(&isInserted)
if err != nil {
return err
}
if isInserted {
return a.InsertObjects(tx)
}
return nil
}
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}
var isInserted bool
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&isInserted)
if err != nil {
return err
}
if isInserted {
return a.InsertObjects(tx)
}
return nil
}

View File

@@ -0,0 +1,52 @@
package db
import (
"context"
"github.com/jmoiron/sqlx"
)
type Addresses struct {
ID uint
Local string
Remote string
RemoteIsAccessible bool
}
func (a *Addresses) Query(data *InsertId[uint]) {
data.query = `
INSERT INTO addresses (game_id, local, remote, remote_is_accessible)
VALUES ($1, $2, $3, $4)
RETURNING id`
}
func (a *Addresses) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, a.Local, a.Remote, a.RemoteIsAccessible}
err := tx.QueryRowx(data.query, args...).Scan(&a.ID)
if err != nil {
return err
}
return nil
}
func (a *Addresses) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, a.Local, a.Remote, a.RemoteIsAccessible}
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&a.ID)
if err != nil {
return err
}
return nil
}

View File

@@ -0,0 +1,55 @@
package db
import (
"context"
"fmt"
"github.com/jmoiron/sqlx"
)
type Author struct {
ID uint
Name string
URL string
Email string
Discord string
// SystemAuthorsFlags SystemAuthorsFlags `json:"flags"`
}
func (a *Author) Query(data *InsertId[string]) {
data.query = fmt.Sprintf(`
INSERT INTO author (%s, name, url, email, discord)
VALUES ($1, $2, $3, $4, $5)
RETURNING id`, data.fieldName)
}
func (a *Author) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, a.Name, a.URL, a.Email, a.Discord}
err := tx.QueryRowx(data.query, args...).Scan(&a.ID)
if err != nil {
return err
}
return nil
}
func (a *Author) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, a.Name, a.URL, a.Email, a.Discord}
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&a.ID)
if err != nil {
return err
}
return nil
}

View File

@@ -0,0 +1,263 @@
package db
import (
"context"
"database/sql"
"errors"
"github.com/jmoiron/sqlx"
"golang.org/x/sync/errgroup"
)
type CardDeck struct {
ID string
Name string
Type string
Description string
Img string
Folder string
Width int
Height int
Rotation int
Sort int
DisplayCount bool
Stats Stats
Ownership []OwnershipString
Cards []*Card
}
func (c *CardDeck) Query(data *InsertId[uint]) {
data.query = `
INSERT INTO card_deck (game_id, id, name, type, description, img, folder, width, height, rotation, sort, display_count)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
ON CONFLICT(id) DO UPDATE SET
game_id = EXCLUDED.game_id,
updated_at = datetime('now')
RETURNING (created_at == updated_at) AS is_inserted`
}
func (c *CardDeck) InsertObjects(tx *sqlx.Tx) error {
group, ctx := errgroup.WithContext(context.Background())
relId := InsertId[string]{id: c.ID, fieldName: "card_deck_id"}
InsertWithCtxParallel(group, ctx, tx, c.Stats, relId)
InsertSliceParallel(group, tx, c.Ownership, relId)
InsertSliceParallel(group, tx, c.Cards, relId)
err := group.Wait()
if err != nil && !errors.Is(err, sql.ErrNoRows) {
return err
}
return nil
}
func (c *CardDeck) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, c.ID, c.Name, c.Type, c.Description, c.Img, c.Folder, c.Width, c.Height, c.Rotation, c.Sort, c.DisplayCount}
var isInserted bool
err := tx.QueryRowx(data.query, args...).Scan(&isInserted)
if err != nil {
return err
}
if isInserted {
return c.InsertObjects(tx)
}
return nil
}
func (c *CardDeck) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, c.ID, c.Name, c.Type, c.Description, c.Img, c.Folder, c.Width, c.Height, c.Rotation, c.Sort, c.DisplayCount}
var isInserted bool
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&isInserted)
if err != nil {
return err
}
if isInserted {
return c.InsertObjects(tx)
}
return nil
}
type Card struct {
ID string
Name string
Type string
Suit string
Description string
Origin string
Width int
Height int
Rotation int
Value int
Face int
Sort int
Drawn bool
Back Back
Stats Stats
Faces []Face
}
func (c *Card) Query(data *InsertId[string]) {
data.query = `
INSERT INTO card (card_deck_id, id, name, type, suit, description, origin, width, height, rotation, value, face, sort, drawn)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)
ON CONFLICT(id) DO UPDATE SET
card_deck_id = EXCLUDED.card_deck_id,
updated_at = datetime('now')
RETURNING (created_at == updated_at) AS is_inserted`
}
func (c *Card) InsertObjects(tx *sqlx.Tx) error {
group, ctx := errgroup.WithContext(context.Background())
relId := InsertId[string]{id: c.ID, fieldName: "card_id"}
InsertWithCtxParallel(group, ctx, tx, c.Back, relId)
InsertWithCtxParallel(group, ctx, tx, c.Stats, relId)
InsertSliceParallel(group, tx, c.Faces, relId)
err := group.Wait()
if err != nil && !errors.Is(err, sql.ErrNoRows) {
return err
}
return nil
}
func (c *Card) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, c.ID, c.Name, c.Type, c.Suit, c.Description, c.Origin, c.Width, c.Height, c.Rotation, c.Value, c.Face, c.Sort, c.Drawn}
var isInserted bool
err := tx.QueryRowx(data.query, args...).Scan(&isInserted)
if err != nil {
return err
}
if isInserted {
return c.InsertObjects(tx)
}
return nil
}
func (c *Card) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, c.ID, c.Name, c.Type, c.Suit, c.Description, c.Origin, c.Width, c.Height, c.Rotation, c.Value, c.Face, c.Sort, c.Drawn}
var isInserted bool
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&isInserted)
if err != nil {
return err
}
if isInserted {
return c.InsertObjects(tx)
}
return nil
}
type Face struct {
ID uint
Name string
Img string
Text string
}
func (f Face) Query(data *InsertId[string]) {
data.query = `
INSERT INTO face (card_id, name, img, text)
VALUES ($1, $2, $3, $4)
RETURNING id`
}
func (f Face) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, f.Name, f.Img, f.Text}
err := tx.QueryRowx(data.query, args...).Scan(&f.ID)
if err != nil {
return err
}
return nil
}
func (f Face) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, f.Name, f.Img, f.Text}
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&f.ID)
if err != nil {
return err
}
return nil
}
type Back struct {
ID uint
Name string
Text string
}
func (b Back) Query(data *InsertId[string]) {
data.query = `
INSERT INTO back (card_id, name, text)
VALUES ($1, $2, $3)
RETURNING id`
}
func (b Back) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, b.Name, b.Text}
err := tx.QueryRowx(data.query, args...).Scan(&b.ID)
if err != nil {
return err
}
return nil
}
func (b Back) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, b.Name, b.Text}
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&b.ID)
if err != nil {
return err
}
return nil
}

View File

@@ -0,0 +1,163 @@
package db
import (
"context"
"database/sql"
"errors"
"github.com/jmoiron/sqlx"
"golang.org/x/sync/errgroup"
)
type Combat struct {
ID string
Type string
Scene string
Round int
Turn int
Sort int
Active bool
Stats Stats
Groups []string
Combatants []*Combatant
}
func (c *Combat) Query(data *InsertId[uint]) {
data.query = `
INSERT INTO combat (game_id, id, type, scene, round, turn, sort, active)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
ON CONFLICT(id) DO UPDATE SET
game_id = EXCLUDED.game_id,
updated_at = datetime('now')
RETURNING (created_at == updated_at) AS is_inserted`
}
func (c *Combat) InsertObjects(tx *sqlx.Tx) error {
group, ctx := errgroup.WithContext(context.Background())
relId := InsertId[string]{id: c.ID, fieldName: "combat_id", tableName: "combat_groups"}
InsertWithCtxParallel(group, ctx, tx, c.Stats, relId)
InsertSimpleSliceParallel(group, tx, c.Groups, &relId)
InsertSliceParallel(group, tx, c.Combatants, relId)
err := group.Wait()
if err != nil && !errors.Is(err, sql.ErrNoRows) {
return err
}
return nil
}
func (c *Combat) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, c.ID, c.Type, c.Scene, c.Round, c.Turn, c.Sort, c.Active}
var isInserted bool
err := tx.QueryRowx(data.query, args...).Scan(&isInserted)
if err != nil {
return err
}
if isInserted {
return c.InsertObjects(tx)
}
return nil
}
func (c *Combat) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, c.ID, c.Type, c.Scene, c.Round, c.Turn, c.Sort, c.Active}
var isInserted bool
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&isInserted)
if err != nil {
return err
}
if isInserted {
return c.InsertObjects(tx)
}
return nil
}
type Combatant struct {
ID string
TokenId string
SceneId string
ActorId string
Type string
Img string
Group string
Initiative int
Hidden bool
Defeated bool
Stats Stats
}
func (c *Combatant) Query(data *InsertId[string]) {
data.query = `
INSERT INTO combatant (combat_id, id, token_id, scene_id, actor_id, type, img, group_, initiative, hidden, defeated)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
ON CONFLICT(id) DO UPDATE SET
combat_id = EXCLUDED.combat_id,
updated_at = datetime('now')
RETURNING (created_at == updated_at) AS is_inserted`
}
func (c *Combatant) InsertObjects(tx *sqlx.Tx) error {
relId := InsertId[string]{id: c.ID, fieldName: "combatant_id"}
group, ctx := errgroup.WithContext(context.Background())
InsertWithCtxParallel(group, ctx, tx, c.Stats, relId)
err := group.Wait()
if err != nil && !errors.Is(err, sql.ErrNoRows) {
return err
}
return nil
}
func (c *Combatant) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, c.ID, c.TokenId, c.SceneId, c.ActorId, c.Type, c.Img, c.Group, c.Initiative, c.Hidden, c.Defeated}
var isInserted bool
err := tx.QueryRowx(data.query, args...).Scan(&isInserted)
if err != nil {
return err
}
if isInserted {
return c.InsertObjects(tx)
}
return nil
}
func (c *Combatant) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, c.ID, c.TokenId, c.SceneId, c.ActorId, c.Type, c.Img, c.Group, c.Initiative, c.Hidden, c.Defeated}
var isInserted bool
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&isInserted)
if err != nil {
return err
}
if isInserted {
return c.InsertObjects(tx)
}
return nil
}

View File

@@ -0,0 +1,53 @@
package db
import (
"context"
"fmt"
"github.com/jmoiron/sqlx"
)
type Compatibility struct {
ID uint
Minimum string
Verified string
Maximum string
}
func (c Compatibility) Query(data *InsertId[string]) {
data.query = fmt.Sprintf(`
INSERT INTO compatibility (%s, minimum, verified, maximum)
VALUES ($1, $2, $3, $4)
RETURNING id`, data.fieldName)
}
func (c Compatibility) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, c.Minimum, c.Verified, c.Maximum}
err := tx.QueryRowx(data.query, args...).Scan(&c.ID)
if err != nil {
return err
}
return nil
}
func (c Compatibility) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, c.Minimum, c.Verified, c.Maximum}
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&c.ID)
if err != nil {
return err
}
return nil
}

View File

@@ -0,0 +1,7 @@
package db
type DetailsLanguages struct {
ID uint
Details string
}

View File

@@ -0,0 +1,120 @@
package db
import (
"context"
"database/sql"
"errors"
"fmt"
"github.com/jmoiron/sqlx"
"golang.org/x/sync/errgroup"
)
type DocumentTypes struct {
ID uint
Data []*DocumentTypeData
}
func (d DocumentTypes) Query(data *InsertId[string]) {
data.query = fmt.Sprintf(`
INSERT INTO document_types (%s)
VALUES ($1)
RETURNING id`, data.fieldName)
}
func (d DocumentTypes) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id}
err := tx.QueryRowx(data.query, args...).Scan(&d.ID)
if err != nil {
return err
}
group, _ := errgroup.WithContext(context.Background())
InsertSliceParallel(group, tx, d.Data, InsertId[uint]{id: d.ID})
err = group.Wait()
if err != nil && !errors.Is(err, sql.ErrNoRows) {
return err
}
return nil
}
func (d DocumentTypes) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id}
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&d.ID)
if err != nil {
return err
}
group, _ := errgroup.WithContext(context.Background())
InsertSliceParallel(group, tx, d.Data, InsertId[uint]{id: d.ID})
err = group.Wait()
if err != nil && !errors.Is(err, sql.ErrNoRows) {
return err
}
return nil
}
type DocumentTypeData struct {
ID uint
Type string
HtmlFields []string
}
func (d *DocumentTypeData) Query(data *InsertId[uint]) {
data.query = `
INSERT INTO document_types_data (document_types_id, type)
VALUES ($1, $2)
RETURNING id`
}
func (d *DocumentTypeData) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, d.Type}
err := tx.QueryRowx(data.query, args...).Scan(&d.ID)
if err != nil {
return err
}
group, _ := errgroup.WithContext(context.Background())
relData := &InsertId[uint]{id: d.ID, fieldName: "document_types_data_id", tableName: "document_types_data_html"}
InsertSimpleSliceParallel(group, tx, d.HtmlFields, relData)
return group.Wait()
}
func (d *DocumentTypeData) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, d.Type}
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&d.ID)
if err != nil {
return err
}
group, _ := errgroup.WithContext(context.Background())
relData := &InsertId[uint]{id: d.ID, fieldName: "document_types_data_id", tableName: "document_types_data_html"}
InsertSimpleSliceParallel(group, tx, d.HtmlFields, relData)
return group.Wait()
}

View File

@@ -0,0 +1,44 @@
package db
type Environment struct {
ID uint
GlobalLight EnvironmentGlobalLight
DarknessLevel float64
DarknessLock bool
Cycle bool
ScenesEnvironmentBase EnvironmentBase
Dark EnvironmentBase
}
type EnvironmentGlobalLight struct {
ID uint
Enabled bool
Darkness GlobalLightDarkness
Alpha float64
Bright bool
Color string
Coloration float64
Luminosity float64
Saturation float64
Contrast float64
Shadows float64
}
type EnvironmentBase struct {
ID uint
Hue float64
Intensity float64
Luminosity float64
Saturation float64
Shadows float64
}
type GlobalLightDarkness struct {
ID uint
Max int
Min int
}

View File

@@ -1,9 +0,0 @@
package db
import "errors"
var (
ErrorSetupMoreThanOne = errors.New("Passed more than one setupData")
ErrorSetupNotFound = errors.New("Not found setup data from your request")
ErrorRecordNotFound = errors.New("Record not found")
)

View File

@@ -0,0 +1,113 @@
package db
import (
"context"
"database/sql"
"errors"
"fmt"
"github.com/jmoiron/sqlx"
"golang.org/x/sync/errgroup"
)
type Files struct {
ID uint
Storages []FilesStorage
}
func (f *Files) Query(data *InsertId[uint]) {
data.query = fmt.Sprintf(`
INSERT INTO files (%s)
VALUES ($1)
RETURNING id`, data.fieldName)
}
func (f *Files) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id}
err := tx.QueryRowx(data.query, args...).Scan(&f.ID)
if err != nil {
return err
}
group, _ := errgroup.WithContext(context.Background())
InsertSliceParallel(group, tx, f.Storages, InsertId[uint]{id: f.ID})
err = group.Wait()
if err != nil && !errors.Is(err, sql.ErrNoRows) {
return err
}
return nil
}
func (f *Files) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id}
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&f.ID)
if err != nil {
return err
}
group, _ := errgroup.WithContext(context.Background())
InsertSliceParallel(group, tx, f.Storages, InsertId[uint]{id: f.ID})
err = group.Wait()
if err != nil && !errors.Is(err, sql.ErrNoRows) {
return err
}
return nil
}
type FilesStorage struct {
ID uint
Storage string
}
func (f FilesStorage) Query(data *InsertId[uint]) {
data.query = `
INSERT INTO files_storage (files_id, storage)
VALUES ($1, $2)
RETURNING id`
}
func (f FilesStorage) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, f.Storage}
err := tx.QueryRowx(data.query, args...).Scan(&f.ID)
if err != nil {
return err
}
return nil
}
func (f FilesStorage) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, f.Storage}
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&f.ID)
if err != nil {
return err
}
return nil
}

View File

@@ -0,0 +1,151 @@
package db
import (
"context"
"database/sql"
"errors"
"fmt"
"strconv"
"github.com/jmoiron/sqlx"
"golang.org/x/sync/errgroup"
)
type Folder struct {
ID uint
Name string
Sorting string
Color string
Packs []string
Folders []*Folder
}
func (f *Folder) Query(data *InsertId[string]) {
data.query = fmt.Sprintf(`
INSERT INTO folder (%s, name, sorting, color)
VALUES ($1, $2, $3, $4)
RETURNING id`, data.fieldName)
}
func (f *Folder) InsertObjects(tx *sqlx.Tx) error {
relId := InsertId[string]{
id: strconv.FormatUint(uint64(f.ID), 10),
fieldName: "folder_id",
tableName: "folder_packs",
}
group, _ := errgroup.WithContext(context.Background())
InsertSimpleSliceParallel(group, tx, f.Packs, &relId)
InsertSliceParallel(group, tx, f.Folders, relId)
err := group.Wait()
if err != nil && !errors.Is(err, sql.ErrNoRows) {
return err
}
return nil
}
func (f *Folder) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, f.Name, f.Sorting, f.Color}
err := tx.QueryRowx(data.query, args...).Scan(&f.ID)
if err != nil {
return err
}
return f.InsertObjects(tx)
}
func (f *Folder) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, f.Name, f.Sorting, f.Color}
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&f.ID)
if err != nil {
return err
}
return f.InsertObjects(tx)
}
type WorldFolder struct {
ID string
Name string
Type string
Folder string
Sorting string
Description string
Color string
Sort int
Stats Stats
}
func (w *WorldFolder) Query(data *InsertId[uint]) {
data.query = `
INSERT INTO world_folder (game_id, id, name, type, folder, sorting, description, color, sort)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
ON CONFLICT(id) DO UPDATE SET
game_id = EXCLUDED.game_id,
updated_at = datetime('now')
RETURNING (created_at == updated_at) AS is_inserted`
}
func (w *WorldFolder) InsertObjects(tx *sqlx.Tx) error {
group, ctx := errgroup.WithContext(context.Background())
relId := InsertId[string]{id: w.ID, fieldName: "world_folder_id"}
InsertWithCtxParallel(group, ctx, tx, w.Stats, relId)
err := group.Wait()
if err != nil && !errors.Is(err, sql.ErrNoRows) {
return err
}
return nil
}
func (w *WorldFolder) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, w.ID, w.Name, w.Type, w.Folder, w.Sorting, w.Description, w.Color, w.Sort}
var isInserted bool
err := tx.QueryRowx(data.query, args...).Scan(&isInserted)
if err != nil {
return err
}
if isInserted {
return w.InsertObjects(tx)
}
return nil
}
func (w *WorldFolder) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, w.ID, w.Name, w.Type, w.Folder, w.Sorting, w.Description, w.Color, w.Sort}
var isInserted bool
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&isInserted)
if err != nil {
return err
}
if isInserted {
return w.InsertObjects(tx)
}
return nil
}

View File

@@ -0,0 +1,26 @@
package db
import (
"database/sql"
"time"
)
type FoundryDataType string
const (
SetupType = FoundryDataType("setup")
GameType = FoundryDataType("game")
)
type FoundryData struct {
ID uint
Type FoundryDataType
Setup *Setup
Game *Game
CreatedAt time.Time
}
type FoundryDataModel struct {
DB *sql.DB
}

View File

@@ -1,308 +0,0 @@
package db
import (
"context"
"database/sql"
"errors"
"time"
"github.com/jmoiron/sqlx"
)
type StateType int
const (
AuthState = StateType(0)
SetupState = StateType(1)
JoinState = StateType(2)
PlayersState = StateType(3)
UpdateState = StateType(4)
LicenseState = StateType(5)
)
type FoundryState struct {
Id int64
IsAdmin bool
IsSetup bool
Type StateType
CreatedAt time.Time
Options Options
Modules Modules
Systems Systems
Worlds Worlds
Users Users
}
type Compatibility struct {
Id int64
Minimum string
Verified string
Maximum string
}
type Options struct {
Id int64
Language string
}
type FoundryStateModel struct {
DB *sqlx.DB
}
func (m FoundryStateModel) Insert(state *FoundryState) error {
query := `
INSERT INTO foundry_state (is_admin, is_setup, state_type)
VALUES ($1, $2, $3)
RETURNING id, created_at`
args := []any{state.IsAdmin, state.IsSetup, state.Type}
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
err := m.DB.QueryRowContext(ctx, query, args...).Scan(&state.Id, &state.CreatedAt)
if err != nil {
return err
}
err = m.InsertOptions(&state.Options, state.Id)
if err != nil && err != sql.ErrNoRows {
return err
}
for i := range state.Modules {
err = m.InsertModule(&state.Modules[i], state.Id)
if err != nil && err != sql.ErrNoRows {
return err
}
}
for i := range state.Systems {
err = m.InsertSystem(&state.Systems[i], state.Id)
if err != nil && err != sql.ErrNoRows {
return err
}
}
for i := range state.Worlds {
err = m.InsertWorld(&state.Worlds[i], state.Id)
if err != nil && err != sql.ErrNoRows {
return err
}
}
for i := range state.Users {
err = m.InsertUser(&state.Users[i], state.Id)
if err != nil && err != sql.ErrNoRows {
return err
}
}
return nil
}
func (m FoundryStateModel) InsertOptions(options *Options, stateId int64) error {
query := `
INSERT INTO options (state_id, lang)
VALUES ($1, $2)
RETURNING id`
args := []any{stateId, options.Language}
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
return m.DB.QueryRowContext(ctx, query, args...).Scan(&options.Id)
}
func (m FoundryStateModel) Get(id int64) (*FoundryState, error) {
if id < 1 {
return nil, ErrorRecordNotFound
}
query := `
SELECT id, created_at, is_admin, is_setup, state_type
FROM foundry_state
WHERE id = $1`
var state FoundryState
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
err := m.DB.QueryRowContext(ctx, query, id).Scan(
&state.Id,
&state.CreatedAt,
&state.IsAdmin,
&state.IsSetup,
&state.Type,
)
if err != nil {
switch {
case errors.Is(err, sql.ErrNoRows):
return nil, ErrorRecordNotFound
default:
return nil, err
}
}
state.Modules, err = m.GetModules(state.Id)
if err != nil {
return nil, err
}
state.Systems, err = m.GetSystems(state.Id)
if err != nil {
return nil, err
}
state.Worlds, err = m.GetWorlds(state.Id)
if err != nil {
return nil, err
}
state.Users, err = m.GetUsers(state.Id)
if err != nil {
return nil, err
}
options, err := m.GetOptions(state.Id)
if err != nil {
return nil, err
}
state.Options = *options
return &state, nil
}
func (m FoundryStateModel) GetOptions(idState int64) (*Options, error) {
if idState < 1 {
return nil, ErrorRecordNotFound
}
query := `
SELECT id, lang
FROM options
WHERE state_id = $1`
var options Options
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
err := m.DB.QueryRowContext(ctx, query, idState).Scan(
&options.Id,
&options.Language,
)
if err != nil {
switch {
case errors.Is(err, sql.ErrNoRows):
return nil, ErrorRecordNotFound
default:
return nil, err
}
}
return &options, nil
}
func (m FoundryStateModel) GetIdByType(stateType StateType) (int64, error) {
if stateType < 0 {
return -1, ErrorRecordNotFound
}
query := `
SELECT id
FROM foundry_state
WHERE state_type = $1`
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
var id int64
err := m.DB.QueryRowContext(ctx, query, stateType).Scan(&id)
if err != nil {
switch {
case errors.Is(err, sql.ErrNoRows):
return -1, ErrorRecordNotFound
default:
return -1, err
}
}
return id, nil
}
func (m FoundryStateModel) Delete(id int64) error {
if id < 1 {
return ErrorRecordNotFound
}
query := `
DELETE FROM foundry_state
WHERE id = $1`
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
result, err := m.DB.ExecContext(ctx, query, id)
if err != nil {
return err
}
rowsAffected, err := result.RowsAffected()
if err != nil {
return err
}
if rowsAffected == 0 {
return ErrorRecordNotFound
}
return nil
}
func (m FoundryStateModel) DeleteAll() error {
query := `
DELETE FROM foundry_state`
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
result, err := m.DB.ExecContext(ctx, query)
if err != nil {
return err
}
rowsAffected, err := result.RowsAffected()
if err != nil {
return err
}
if rowsAffected == 0 {
return ErrorRecordNotFound
}
return nil
}
func (m FoundryStateModel) DeleteAllSeq() error {
query := `
DELETE FROM sqlite_sequence`
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
result, err := m.DB.ExecContext(ctx, query)
if err != nil {
return err
}
rowsAffected, err := result.RowsAffected()
if err != nil {
return err
}
if rowsAffected == 0 {
return ErrorRecordNotFound
}
return nil
}

View File

@@ -0,0 +1,124 @@
package db
import (
"context"
"database/sql"
"errors"
"strconv"
"time"
"github.com/jmoiron/sqlx"
"golang.org/x/sync/errgroup"
)
type Game struct {
ID uint
CreatedAt time.Time
DemoMode bool
IdleLogout bool
Paused bool
UserID string
Addresses Addresses
Files Files
Options GameOptions
Release Release
World *World
System *System
CoreUpdate CoreUpdate
SystemUpdate SystemUpdate
ActiveUsers []string
Modules []*Module
PackageWarnings []*PackageWarning
Packs []*Pack
Messages []*Message
Combats []*Combat
CardDeck []*CardDeck
Users []*User
Macros []*Macro
Folders []*WorldFolder
Items []*Item
Settings []*Setting
Journals []*Journal
Tables []*Table
Playlists []*Playlist
Actors []*Actor
// Scenes []Scene
}
func (g *Game) InsertObjects(tx *sqlx.Tx) error {
relData := InsertId[uint]{id: g.ID, fieldName: "game_id"}
group, ctx := errgroup.WithContext(context.Background())
InsertWithCtxParallel(group, ctx, tx, &g.Addresses, relData)
InsertWithCtxParallel(group, ctx, tx, &g.Files, relData)
InsertWithCtxParallel(group, ctx, tx, &g.Options, relData)
InsertWithCtxParallel(group, ctx, tx, &g.Release, relData)
InsertWithCtxParallel(group, ctx, tx, &g.CoreUpdate, relData)
InsertWithCtxParallel(group, ctx, tx, &g.SystemUpdate, relData)
relDataString := InsertId[string]{id: strconv.FormatUint(uint64(g.ID), 10), fieldName: "game_id"}
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"})
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.Settings, relData)
InsertSliceParallel(group, tx, g.Journals, relData)
InsertSliceParallel(group, tx, g.Tables, relData)
InsertSliceParallel(group, tx, g.Playlists, relData)
err := group.Wait()
if err != nil && !errors.Is(err, sql.ErrNoRows) {
return err
}
InsertSliceParallelTimeout(group, tx, g.Items,
InsertId[string]{id: strconv.FormatUint(uint64(g.ID), 10), fieldName: "game_id"},
15*time.Second)
InsertSliceParallelTimeout(group, tx, g.Actors, relData, 15*time.Second)
err = group.Wait()
if err != nil && !errors.Is(err, sql.ErrNoRows) {
return err
}
return nil
}
func (g *Game) Insert(db *sqlx.DB) error {
tx := db.MustBegin()
defer tx.Rollback()
const query = `
INSERT INTO game (demo_mode, idle_logout, paused, user_id)
VALUES ($1, $2, $3, $4)
RETURNING id, created_at`
args := []any{g.DemoMode, g.IdleLogout, g.Paused, g.UserID}
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
err := tx.QueryRowxContext(ctx, query, args...).Scan(&g.ID, &g.CreatedAt)
if err != nil {
return err
}
err = g.InsertObjects(tx)
if err != nil {
return err
}
return tx.Commit()
}

View File

@@ -0,0 +1,61 @@
package db
import (
"context"
"github.com/jmoiron/sqlx"
)
type Grid struct {
ID uint
Type int
Size int
Distance int
Diagonals int
Thickness int
Alpha float64
Color string
Units string
Style string
}
func (g *Grid) Query(data *InsertId[string]) {
data.query = `
INSERT INTO grid (system_id, type, size, distance, diagonals, thickness,
alpha, color, units, style)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
RETURNING id`
}
func (g *Grid) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, g.Type, g.Size, g.Distance, g.Diagonals, g.Thickness,
g.Alpha, g.Color, g.Units, g.Style}
err := tx.QueryRowx(data.query, args...).Scan(&g.ID)
if err != nil {
return err
}
return nil
}
func (g *Grid) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, g.Type, g.Size, g.Distance, g.Diagonals, g.Thickness,
g.Alpha, g.Color, g.Units, g.Style}
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&g.ID)
if err != nil {
return err
}
return nil
}

View File

@@ -0,0 +1,88 @@
package db
import (
"context"
"github.com/jmoiron/sqlx"
)
type Index struct {
ID string
Folder string
Img string
Name string
Type string
}
func (i *Index) Query(data *InsertId[string]) {
data.query = `
INSERT INTO index_ (id, folder, img, name, type)
VALUES ($1, $2, $3, $4, $5)
ON CONFLICT(id) DO NOTHING`
}
func (i *Index) ConnectGameQuery(data *InsertId[string]) {
data.query = `
INSERT INTO pack_to_index (pack_id, index_id)
VALUES ($1, $2)
ON CONFLICT(pack_id, index_id) DO NOTHING`
}
func (i *Index) ConnectGame(tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, i.ID}
_, err := tx.Exec(data.query, args...)
return err
}
func (i *Index) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{i.ID, i.Folder, i.Img, i.Name, i.Type}
_, err := tx.Exec(data.query, args...)
if err != nil {
return err
}
dataCopy := *data
i.ConnectGameQuery(&dataCopy)
return i.ConnectGame(tx, &dataCopy)
}
func (i *Index) ConnectGameCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, i.ID}
_, err := tx.ExecContext(ctx, data.query, args...)
return err
}
func (i *Index) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{i.ID, i.Folder, i.Img, i.Name, i.Type}
_, err := tx.ExecContext(ctx, data.query, args...)
if err != nil {
return err
}
dataCopy := *data
i.ConnectGameQuery(&dataCopy)
return i.ConnectGameCtx(ctx, tx, &dataCopy)
}

View File

@@ -0,0 +1,85 @@
package db
import (
"context"
"database/sql"
"errors"
"fmt"
"github.com/jmoiron/sqlx"
"golang.org/x/sync/errgroup"
)
type Item struct {
ID string
Img string
Name string
Type string
Folder string
Sort int
Stats Stats
Ownership []OwnershipString
}
func (i *Item) Query(data *InsertId[string]) {
data.query = fmt.Sprintf(`
INSERT INTO item (%[1]s, id, img, name, type, folder, sort)
VALUES ($1, $2, $3, $4, $5, $6, $7)
ON CONFLICT(id) DO UPDATE SET
%[1]s = EXCLUDED.%[1]s,
updated_at = datetime('now')
RETURNING (created_at == updated_at) AS is_inserted`, data.fieldName)
}
func (i *Item) InsertObjects(tx *sqlx.Tx) error {
group, ctx := errgroup.WithContext(context.Background())
relId := InsertId[string]{id: i.ID, fieldName: "item_id"}
InsertWithCtxParallel(group, ctx, tx, i.Stats, relId)
InsertSliceParallel(group, tx, i.Ownership, relId)
err := group.Wait()
if err != nil && !errors.Is(err, sql.ErrNoRows) {
return err
}
return nil
}
func (i *Item) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, i.ID, i.Img, i.Name, i.Type, i.Folder, i.Sort}
var isInserted bool
err := tx.QueryRowx(data.query, args...).Scan(&isInserted)
if err != nil {
return err
}
if isInserted {
return i.InsertObjects(tx)
}
return nil
}
func (i *Item) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, i.ID, i.Img, i.Name, i.Type, i.Folder, i.Sort}
var isInserted bool
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&isInserted)
if err != nil {
return err
}
if isInserted {
return i.InsertObjects(tx)
}
return nil
}

View File

@@ -0,0 +1,81 @@
package db
import (
"context"
"database/sql"
"errors"
"github.com/jmoiron/sqlx"
"golang.org/x/sync/errgroup"
)
type Journal struct {
ID string
Name string
Sort int
Pages []*JournalPage
Ownership []OwnershipString
}
func (j *Journal) Query(data *InsertId[uint]) {
data.query = `
INSERT INTO journal (game_id, id, name, sort)
VALUES ($1, $2, $3, $4)
ON CONFLICT(id) DO UPDATE SET
game_id = EXCLUDED.game_id,
updated_at = datetime('now')
RETURNING (created_at == updated_at) AS is_inserted`
}
func (j *Journal) InsertObjects(tx *sqlx.Tx) error {
group, _ := errgroup.WithContext(context.Background())
relId := InsertId[string]{id: j.ID, fieldName: "journal_id"}
InsertSliceParallel(group, tx, j.Pages, relId)
InsertSliceParallel(group, tx, j.Ownership, relId)
err := group.Wait()
if err != nil && !errors.Is(err, sql.ErrNoRows) {
return err
}
return nil
}
func (j *Journal) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, j.ID, j.Name, j.Sort}
var isInserted bool
err := tx.QueryRowx(data.query, args...).Scan(&isInserted)
if err != nil {
return err
}
if isInserted {
return j.InsertObjects(tx)
}
return nil
}
func (j *Journal) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, j.ID, j.Name, j.Sort}
var isInserted bool
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&isInserted)
if err != nil {
return err
}
if isInserted {
return j.InsertObjects(tx)
}
return nil
}

View File

@@ -0,0 +1,221 @@
package db
import (
"context"
"database/sql"
"errors"
"fmt"
"github.com/jmoiron/sqlx"
"golang.org/x/sync/errgroup"
)
type JournalPage struct {
ID string
Name string
Type string
Src string
Sort int
Text PageText
Title PageTitle
Video PageVideo
Stats Stats
Ownership []OwnershipString
}
func (j *JournalPage) Query(data *InsertId[string]) {
data.query = `
INSERT INTO journal_page (journal_id, id, name, type, src, sort)
VALUES ($1, $2, $3, $4, $5, $6)
ON CONFLICT(id) DO UPDATE SET
journal_id = EXCLUDED.journal_id,
updated_at = datetime('now')
RETURNING (created_at == updated_at) AS is_inserted`
}
func (j *JournalPage) InsertObjects(tx *sqlx.Tx) error {
group, ctx := errgroup.WithContext(context.Background())
relId := InsertId[string]{id: j.ID, fieldName: "journal_page_id"}
InsertWithCtxParallel(group, ctx, tx, j.Text, relId)
InsertWithCtxParallel(group, ctx, tx, j.Title, relId)
InsertWithCtxParallel(group, ctx, tx, j.Video, relId)
InsertWithCtxParallel(group, ctx, tx, j.Stats, relId)
InsertSliceParallel(group, tx, j.Ownership, relId)
err := group.Wait()
if err != nil && !errors.Is(err, sql.ErrNoRows) {
return err
}
return nil
}
func (j *JournalPage) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, j.ID, j.Name, j.Type, j.Src, j.Sort}
var isInserted bool
err := tx.QueryRowx(data.query, args...).Scan(&isInserted)
if err != nil {
return err
}
if isInserted {
return j.InsertObjects(tx)
}
return nil
}
func (j *JournalPage) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, j.ID, j.Name, j.Type, j.Src, j.Sort}
var isInserted bool
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&isInserted)
if err != nil {
return err
}
if isInserted {
return j.InsertObjects(tx)
}
return nil
}
type PageText struct {
ID uint
Content string
Markdown string
Format int
}
func (p PageText) Query(data *InsertId[string]) {
data.query = fmt.Sprintf(`
INSERT INTO journal_page_text (%s, content, markdown, format)
VALUES ($1, $2, $3, $4)`, data.fieldName)
}
func (p PageText) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, p.Content, p.Markdown, p.Format}
_, err := tx.Exec(data.query, args...)
if err != nil {
return err
}
return nil
}
func (p PageText) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, p.Content, p.Markdown, p.Format}
_, err := tx.ExecContext(ctx, data.query, args...)
if err != nil {
return err
}
return nil
}
type PageTitle struct {
ID uint
Show bool
Level int
}
func (p PageTitle) Query(data *InsertId[string]) {
data.query = fmt.Sprintf(`
INSERT INTO journal_page_title (%s, show, level)
VALUES ($1, $2, $3)`, data.fieldName)
}
func (p PageTitle) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, p.Show, p.Level}
_, err := tx.Exec(data.query, args...)
if err != nil {
return err
}
return nil
}
func (p PageTitle) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, p.Show, p.Level}
_, err := tx.ExecContext(ctx, data.query, args...)
if err != nil {
return err
}
return nil
}
type PageVideo struct {
ID uint
Controls bool
Volume float64
}
func (p PageVideo) Query(data *InsertId[string]) {
data.query = fmt.Sprintf(`
INSERT INTO journal_page_video (%s, controls, volume)
VALUES ($1, $2, $3)`, data.fieldName)
}
func (p PageVideo) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, p.Controls, p.Volume}
_, err := tx.Exec(data.query, args...)
if err != nil {
return err
}
return nil
}
func (p PageVideo) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, p.Controls, p.Volume}
_, err := tx.ExecContext(ctx, data.query, args...)
if err != nil {
return err
}
return nil
}

View File

@@ -0,0 +1,154 @@
package db
import (
"context"
"database/sql"
"errors"
"fmt"
"github.com/jmoiron/sqlx"
"golang.org/x/sync/errgroup"
)
type SetupLanguage struct {
ID string
Label string
Modules []SetupLanguageModule
}
func (l *SetupLanguage) Query(data *InsertId[uint]) {
data.query = `
INSERT INTO setup_language (setup_id, label)
VALUES ($1, $2)
RETURNING id`
}
func (l *SetupLanguage) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, l.Label}
err := tx.QueryRowx(data.query, args...).Scan(&l.ID)
if err != nil {
return err
}
group, _ := errgroup.WithContext(context.Background())
InsertSliceParallel(group, tx, l.Modules, InsertId[string]{id: l.ID})
return group.Wait()
}
func (l *SetupLanguage) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, l.Label}
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&l.ID)
if err != nil {
return err
}
group, _ := errgroup.WithContext(context.Background())
InsertSliceParallel(group, tx, l.Modules, InsertId[string]{id: l.ID})
err = group.Wait()
if err != nil && !errors.Is(err, sql.ErrNoRows) {
return err
}
return nil
}
type SetupLanguageModule struct {
ID string
Label string
Path string
}
func (l SetupLanguageModule) Query(data *InsertId[string]) {
data.query = `
INSERT INTO setup_language_module (setup_language_id, id, label, path)
VALUES ($1, $2, $3, $4)
RETURNING id`
}
func (l SetupLanguageModule) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, l.ID, l.Label, l.Path}
_, err := tx.Exec(data.query, args...)
if err != nil {
return err
}
return nil
}
func (l SetupLanguageModule) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, l.ID, l.Label, l.Path}
_, err := tx.ExecContext(ctx, data.query, args...)
if err != nil {
return err
}
return nil
}
type Language struct {
ID uint
Lang string
Name string
Path string
}
func (l *Language) Query(data *InsertId[string]) {
data.query = fmt.Sprintf(`
INSERT INTO language (%s, lang, name, path)
VALUES ($1, $2, $3, $4)
RETURNING id`, data.fieldName)
}
func (l *Language) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, l.Lang, l.Name, l.Path}
err := tx.QueryRowx(data.query, args...).Scan(&l.ID)
if err != nil {
return err
}
return nil
}
func (l *Language) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, l.Lang, l.Name, l.Path}
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&l.ID)
if err != nil {
return err
}
return nil
}

View File

@@ -0,0 +1,174 @@
package db
import (
"context"
"database/sql"
"errors"
"fmt"
"github.com/jmoiron/sqlx"
"golang.org/x/sync/errgroup"
)
type Light struct {
ID uint
Color string
Priority int
Angle int
Negative bool
Alpha float64
Bright float64
Coloration float64
Dim float64
Attenuation float64
Luminosity float64
Saturation float64
Contrast float64
Shadows float64
LightAnimation LightAnimation
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
Speed int
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

@@ -0,0 +1,87 @@
package db
import (
"context"
"database/sql"
"errors"
"github.com/jmoiron/sqlx"
"golang.org/x/sync/errgroup"
)
type Macro struct {
ID string
Command string
Name string
Type string
Img string
Author string
Scope string
Folder string
Sort int
Stats Stats
Ownership []OwnershipString
}
func (m *Macro) Query(data *InsertId[uint]) {
data.query = `
INSERT INTO macro (game_id, id, command, name, type, img, author, scope, folder, sort)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
ON CONFLICT(id) DO UPDATE SET
game_id = EXCLUDED.game_id,
updated_at = datetime('now')
RETURNING (created_at == updated_at) AS is_inserted`
}
func (m *Macro) InsertObjects(tx *sqlx.Tx) error {
group, ctx := errgroup.WithContext(context.Background())
relId := InsertId[string]{id: m.ID, fieldName: "macro_id"}
InsertWithCtxParallel(group, ctx, tx, m.Stats, relId)
InsertSliceParallel(group, tx, m.Ownership, relId)
err := group.Wait()
if err != nil && !errors.Is(err, sql.ErrNoRows) {
return err
}
return nil
}
func (m *Macro) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, m.ID, m.Command, m.Name, m.Type, m.Img, m.Author, m.Scope, m.Folder, m.Sort}
var isInserted bool
err := tx.QueryRowx(data.query, args...).Scan(&isInserted)
if err != nil {
return err
}
if isInserted {
return m.InsertObjects(tx)
}
return nil
}
func (m *Macro) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, m.ID, m.Command, m.Name, m.Type, m.Img, m.Author, m.Scope, m.Folder, m.Sort}
var isInserted bool
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&isInserted)
if err != nil {
return err
}
if isInserted {
return m.InsertObjects(tx)
}
return nil
}

View File

@@ -0,0 +1,53 @@
package db
import (
"context"
"fmt"
"github.com/jmoiron/sqlx"
)
type Media struct {
ID uint
Type string
URL string
Caption string
}
func (m *Media) Query(data *InsertId[string]) {
data.query = fmt.Sprintf(`
INSERT INTO media (%s, type, url, caption)
VALUES ($1, $2, $3, $4)
RETURNING id`, data.fieldName)
}
func (m *Media) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, m.Type, m.URL, m.Caption}
err := tx.QueryRowx(data.query, args...).Scan(&m.ID)
if err != nil {
return err
}
return nil
}
func (m *Media) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, m.Type, m.URL, m.Caption}
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&m.ID)
if err != nil {
return err
}
return nil
}

View File

@@ -0,0 +1,139 @@
package db
import (
"context"
"database/sql"
"errors"
"fmt"
"github.com/jmoiron/sqlx"
"golang.org/x/sync/errgroup"
)
type Message struct {
ID string
Blind bool
Emote bool
Style int
Timestamp int64
Content string
Author string
Type string
Flavor string
Sound string
Stats Stats
Speaker Speaker
Whisper []string
Rolls []string
}
func (m *Message) Query(data *InsertId[uint]) {
data.query = `
INSERT INTO message (game_id, id, blind, emote, style, timestamp, content, author, type, flavor, sound)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
ON CONFLICT(id) DO UPDATE SET
game_id = EXCLUDED.game_id,
updated_at = datetime('now')
RETURNING (created_at == updated_at) AS is_inserted`
}
func (m *Message) InsertObjects(tx *sqlx.Tx) error {
relId := InsertId[string]{id: m.ID, fieldName: "message_id"}
group, ctx := errgroup.WithContext(context.Background())
InsertWithCtxParallel(group, ctx, tx, m.Stats, relId)
InsertWithCtxParallel(group, ctx, tx, m.Speaker, relId)
InsertSimpleSliceParallel(group, tx, m.Whisper, &InsertId[string]{id: m.ID, fieldName: "message_id", tableName: "message_whisper"})
InsertSimpleSliceParallel(group, tx, m.Rolls, &InsertId[string]{id: m.ID, fieldName: "message_id", tableName: "message_rolls"})
err := group.Wait()
if err != nil && !errors.Is(err, sql.ErrNoRows) {
return err
}
return nil
}
func (m *Message) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, m.ID, m.Blind, m.Emote, m.Style, m.Timestamp, m.Content, m.Author, m.Type, m.Flavor, m.Sound}
var isInserted bool
err := tx.QueryRowx(data.query, args...).Scan(&isInserted)
if err != nil {
return err
}
if isInserted {
return m.InsertObjects(tx)
}
return nil
}
func (m *Message) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, m.ID, m.Blind, m.Emote, m.Style, m.Timestamp, m.Content, m.Author, m.Type, m.Flavor, m.Sound}
var isInserted bool
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&isInserted)
if err != nil {
return err
}
if isInserted {
return m.InsertObjects(tx)
}
return nil
}
type Speaker struct {
ID uint
Scene string
Actor string
Token string
Alias string
}
func (s Speaker) Query(data *InsertId[string]) {
data.query = fmt.Sprintf(`
INSERT INTO speaker (%s, scene, actor, token, alias)
VALUES ($1, $2, $3, $4, $5)`, data.fieldName)
}
func (s Speaker) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, s.Scene, s.Actor, s.Token, s.Alias}
err := tx.QueryRowx(data.query, args...).Scan(&s.ID)
if err != nil {
return err
}
return nil
}
func (s Speaker) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, s.Scene, s.Actor, s.Token, s.Alias}
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&s.ID)
if err != nil {
return err
}
return nil
}

View File

@@ -1,35 +0,0 @@
package db
import "github.com/jmoiron/sqlx"
// var (
// ErrRecordNotFound = errors.New("record not found")
// ErrEditConflict = errors.New("edit conflict")
// )
type Models struct {
FoundryState FoundryStateModel
}
// type Models struct {
// Movies interface {
// Insert(movie *Movie) error
// Get(id int64) (*Movie, error)
// Update(movie *Movie) error
// Delete(id int64) error
// GetAll(title string, genres []string, filter Filters) ([]*Movie, Metadata, error)
// }
// }
func NewModels(db *sqlx.DB) *Models {
return &Models{
FoundryState: FoundryStateModel{DB: db},
}
}
// func NewMockModels() Models {
// return Models{
// Movies: MockMovieModel{},
// Users: MockMovieModel{},
// }
// }

View File

@@ -4,287 +4,185 @@ import (
"context"
"database/sql"
"errors"
"time"
"strings"
"github.com/jmoiron/sqlx"
"golang.org/x/sync/errgroup"
)
type Module struct {
Id int64
TextId string
Title string
Description string
Url string
Version string
Availability int
CreatedAt time.Time
Languages []Language
Compatibility Compatibility
ID string
Title string
Description string
URL string
License string
Readme string
Bugs string
Changelog string
Version string
Download string
Manifest string
Socket bool
Protected bool
Exclusive bool
PersistentStorage bool
CoreTranslation bool
Library bool
Locked bool
Owned bool
HasStorage bool
Active bool
Availability int
DocumentTypes DocumentTypes
Relationships Relationships
Compatibility Compatibility
Scripts []string
Esmodules []string
Tags []string
Authors []*Author
Media []*Media
Styles []*Style
Languages []*Language
Packs []*Pack
PackFolders []*Folder
}
type Language struct {
Id int64
Lang string
Name string
Path string
func (m *Module) Query(data *InsertId[uint]) {
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)
ON CONFLICT(id) DO NOTHING`
}
type Modules []Module
func (modules Modules) GetById(id int) *Module {
return &modules[id]
func (m *Module) ConnectGameQuery(data *InsertId[uint]) {
data.query = `
INSERT INTO game_to_module (game_id, module_id)
VALUES ($1, $2)`
}
func (m FoundryStateModel) InsertModule(module *Module, stateId int64) error {
query := `
INSERT INTO modules (state_id, text_id, title, description, url, version, availability)
VALUES ($1, $2, $3, $4, $5, $6, $7)
RETURNING id, created_at`
func (m *Module) InsertObjects(tx *sqlx.Tx) error {
relId := InsertId[string]{id: m.ID, fieldName: "module_id"}
group, ctx := errgroup.WithContext(context.Background())
args := []any{stateId, module.TextId, module.Title, module.Description, module.Url, module.Version, module.Availability}
InsertWithCtxParallel(group, ctx, tx, m.DocumentTypes, relId)
InsertWithCtxParallel(group, ctx, tx, m.Relationships, relId)
InsertWithCtxParallel(group, ctx, tx, m.Compatibility, relId)
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
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)
err := m.DB.QueryRowContext(ctx, query, args...).Scan(&module.Id, &module.CreatedAt)
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) {
return err
}
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
}
var dataId *uint
if strings.EqualFold(data.fieldName, "setup_id") {
dataId = &data.id
}
args := []any{dataId, 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}
res, err := tx.Exec(data.query, args...)
if err != nil {
return err
}
for i := range module.Languages {
err = m.InsertModuleLanguages(&module.Languages[i], module.Id)
if err != nil && err != sql.ErrNoRows {
rowsAffected, err := res.RowsAffected()
if rowsAffected != 0 {
err = m.InsertObjects(tx)
if err != nil {
return err
}
}
return m.InsertModuleCompatibility(&module.Compatibility, module.Id)
}
func (m FoundryStateModel) InsertModuleCompatibility(compatibility *Compatibility, moduleId int64) error {
query := `
INSERT INTO modules_compatibility (module_id, minimum, verified, maximum)
VALUES ($1, $2, $3, $4)
RETURNING id`
args := []any{moduleId, compatibility.Minimum, compatibility.Verified, compatibility.Maximum}
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
err := m.DB.QueryRowContext(ctx, query, args...).Scan(&compatibility.Id)
if err != nil && err != sql.ErrNoRows {
return err
}
return nil
}
func (m FoundryStateModel) InsertModuleLanguages(lang *Language, moduleId int64) error {
query := `
INSERT INTO modules_languages (module_id, language, name, path)
VALUES ($1, $2, $3, $4)
RETURNING id`
args := []any{moduleId, lang.Lang, lang.Name, lang.Path}
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
err := m.DB.QueryRowContext(ctx, query, args...).Scan(&lang.Id)
if err != nil && err != sql.ErrNoRows {
return err
}
return nil
}
func (m FoundryStateModel) GetModules(idState int64) (Modules, error) {
if idState < 1 {
return nil, ErrorRecordNotFound
if dataId == nil {
dataCopy := *data
m.ConnectGameQuery(&dataCopy)
err = m.ConnectGame(tx, &dataCopy)
}
query := `
SELECT id, created_at, text_id, title, description, url, version, availability
FROM modules
WHERE state_id = $1`
return err
}
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
func (m *Module) ConnectGameCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
rows, err := m.DB.QueryContext(ctx, query, idState)
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 {
if data.query == "" {
return ErrNoQuery
}
var dataId *uint
if strings.EqualFold(data.fieldName, "setup_id") {
dataId = &data.id
}
args := []any{dataId, 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}
res, err := tx.ExecContext(ctx, data.query, args...)
if err != nil {
switch {
case errors.Is(err, sql.ErrNoRows):
return nil, ErrorRecordNotFound
default:
return nil, err
}
return err
}
modules := make(Modules, 0, 1)
for rows.Next() {
var module Module
err := rows.Scan(
&module.Id,
&module.CreatedAt,
&module.TextId,
&module.Title,
&module.Description,
&module.Url,
&module.Version,
&module.Availability,
)
rowsAffected, err := res.RowsAffected()
if rowsAffected != 0 {
err = m.InsertObjects(tx)
if err != nil {
return nil, err
return err
}
module.Languages, err = m.GetModuleLanguages(module.Id)
if err != nil {
return nil, err
}
compatibility, err := m.GetModuleCompatibility(module.Id)
if err != nil {
return nil, err
}
module.Compatibility = *compatibility
modules = append(modules, module)
}
if err := rows.Err(); err != nil {
return nil, err
if dataId == nil {
dataCopy := *data
m.ConnectGameQuery(&dataCopy)
err = m.ConnectGameCtx(ctx, tx, &dataCopy)
}
return modules, nil
}
func (m FoundryStateModel) GetModuleCompatibility(idModule int64) (*Compatibility, error) {
if idModule < 1 {
return nil, ErrorRecordNotFound
}
query := `
SELECT id, minimum, verified, maximum
FROM modules_compatibility
WHERE module_id = $1`
var compatibility Compatibility
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
err := m.DB.QueryRowContext(ctx, query, idModule).Scan(
&compatibility.Id,
&compatibility.Minimum,
&compatibility.Verified,
&compatibility.Maximum,
)
if err != nil {
switch {
case errors.Is(err, sql.ErrNoRows):
return nil, ErrorRecordNotFound
default:
return nil, err
}
}
return &compatibility, nil
}
func (m FoundryStateModel) GetModuleLanguages(idModule int64) ([]Language, error) {
if idModule < 1 {
return nil, ErrorRecordNotFound
}
query := `
SELECT id, language, name, path
FROM modules_languages
WHERE module_id = $1`
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
rows, err := m.DB.QueryContext(ctx, query, idModule)
if err != nil {
switch {
case errors.Is(err, sql.ErrNoRows):
return nil, ErrorRecordNotFound
default:
return nil, err
}
}
languages := make([]Language, 0, 1)
for rows.Next() {
var lang Language
err := rows.Scan(
&lang.Id,
&lang.Lang,
&lang.Name,
&lang.Path,
)
if err != nil {
return nil, err
}
languages = append(languages, lang)
}
if err := rows.Err(); err != nil {
return nil, err
}
return languages, nil
}
func (m FoundryStateModel) DeleteModules(idState int64) error {
if idState < 1 {
return ErrorRecordNotFound
}
query := `
DELETE FROM modules
WHERE state_id = $1`
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
result, err := m.DB.ExecContext(ctx, query, idState)
if err != nil {
return err
}
rowsAffected, err := result.RowsAffected()
if err != nil {
return err
}
if rowsAffected == 0 {
return ErrorRecordNotFound
}
return nil
}
func (m FoundryStateModel) DeleteModule(idModule int64) error {
if idModule < 1 {
return ErrorRecordNotFound
}
query := `
DELETE FROM modules
WHERE id = $1`
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
result, err := m.DB.ExecContext(ctx, query, idModule)
if err != nil {
return err
}
rowsAffected, err := result.RowsAffected()
if err != nil {
return err
}
if rowsAffected == 0 {
return ErrorRecordNotFound
}
return nil
return err
}

View File

@@ -0,0 +1,36 @@
package db
type Note struct {
ID string
EntryID string
PageID string
Text string
X float64
Y float64
Global bool
IconSize int
Texture NoteTexture
FontFamily string
FontSize int
TextColor string
TextAnchor int
Elevation int
Sort int
}
type NoteTexture struct {
ID uint
Tint string
Src string
ScaleX int
ScaleY int
OffsetX float64
OffsetY float64
Rotation int
AnchorX float64
AnchorY float64
Fit string
AlphaThreshold int
}

View File

@@ -0,0 +1,116 @@
package db
import (
"context"
"github.com/jmoiron/sqlx"
)
type GameOptions struct {
ID uint
Language string
UpdateChannel string
Port int
}
func (g *GameOptions) Query(data *InsertId[uint]) {
data.query = `
INSERT INTO game_options (game_id, language, update_channel, port)
VALUES ($1, $2, $3, $4)
RETURNING id`
}
func (g *GameOptions) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, g.Language, g.UpdateChannel, g.Port}
err := tx.QueryRowx(data.query, args...).Scan(&g.ID)
if err != nil {
return err
}
return nil
}
func (g *GameOptions) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, g.Language, g.UpdateChannel, g.Port}
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&g.ID)
if err != nil {
return err
}
return nil
}
type SetupOptions struct {
ID uint
CSSTheme string
DataPath string
Hostname string
Language string
LocalHostname string
UpdateChannel string
Port int
CompressSocket bool
CompressStatic bool
Fullscreen bool
HotReload bool
ProxySSL bool
Telemetry bool
Upnp bool
DeleteNEDB bool
NoBackups bool
}
func (s *SetupOptions) Query(data *InsertId[uint]) {
data.query = `
INSERT INTO setup_options (setup_id, css_theme, data_path, hostname, language, local_hostname,
update_channel, port, compress_socket, compress_static, fullscreen, hot_reload, proxy_ssl,
telemetry, upnp, delete_nedb, no_backups)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17)
RETURNING id`
}
func (s *SetupOptions) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, s.CSSTheme, s.DataPath, s.Hostname, s.Language, s.LocalHostname,
s.UpdateChannel, s.Port, s.CompressSocket, s.CompressStatic, s.Fullscreen, s.HotReload,
s.ProxySSL, s.Telemetry, s.Upnp, s.DeleteNEDB, s.NoBackups}
err := tx.QueryRowx(data.query, args...).Scan(&s.ID)
if err != nil {
return err
}
return nil
}
func (s *SetupOptions) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, s.CSSTheme, s.DataPath, s.Hostname, s.Language, s.LocalHostname,
s.UpdateChannel, s.Port, s.CompressSocket, s.CompressStatic, s.Fullscreen, s.HotReload,
s.ProxySSL, s.Telemetry, s.Upnp, s.DeleteNEDB, s.NoBackups}
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&s.ID)
if err != nil {
return err
}
return nil
}

View File

@@ -0,0 +1,97 @@
package db
import (
"context"
"fmt"
"github.com/jmoiron/sqlx"
)
type Ownership struct {
ID uint
Player string
Trusted string
Assistant string
}
func (o Ownership) Query(data *InsertId[string]) {
data.query = fmt.Sprintf(`
INSERT INTO ownership (%s, player, trusted, assistant)
VALUES ($1, $2, $3, $4)
RETURNING id`, data.fieldName)
}
func (o Ownership) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, o.Player, o.Trusted, o.Assistant}
err := tx.QueryRowx(data.query, args...).Scan(&o.ID)
if err != nil {
return err
}
return nil
}
func (o Ownership) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, o.Player, o.Trusted, o.Assistant}
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&o.ID)
if err != nil {
return err
}
return nil
}
type OwnershipString struct {
ID uint
Key string
Value int
}
func (o OwnershipString) Query(data *InsertId[string]) {
data.query = fmt.Sprintf(`
INSERT INTO ownership_string (%s, key_, value)
VALUES ($1, $2, $3)
RETURNING id`, data.fieldName)
}
func (o OwnershipString) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, o.Key, o.Value}
err := tx.QueryRowx(data.query, args...).Scan(&o.ID)
if err != nil {
return err
}
return nil
}
func (o OwnershipString) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, o.Key, o.Value}
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&o.ID)
if err != nil {
return err
}
return nil
}

View File

@@ -0,0 +1,203 @@
package db
import (
"context"
"database/sql"
"errors"
"fmt"
"strings"
"github.com/jmoiron/sqlx"
"golang.org/x/sync/errgroup"
)
type Pack struct {
ID string
Name string
Label string
Banner string
Path string
Type string
System string
PackageType string
PackageName string
Ownership Ownership
Index []*Index
Folders []*PackFolder
}
func (p *Pack) Query(data *InsertId[string]) {
if !strings.EqualFold(data.fieldName, "game_id") {
data.query = fmt.Sprintf(`
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)
ON CONFLICT(id) DO NOTHING`, data.fieldName)
} else {
data.query = `
INSERT INTO pack (module_id, id, name, label, banner, path, type, system, package_type, package_name)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
ON CONFLICT(id) DO NOTHING`
}
}
func (p *Pack) ConnectGameQuery(data *InsertId[string]) {
data.query = `
INSERT INTO game_to_pack (game_id, pack_id)
VALUES ($1, $2)`
}
func (p *Pack) InsertObjects(tx *sqlx.Tx) error {
group, ctx := errgroup.WithContext(context.Background())
InsertWithCtxParallel(group, ctx, tx, p.Ownership,
InsertId[string]{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)
err := group.Wait()
if err != nil && !errors.Is(err, sql.ErrNoRows) {
return err
}
return nil
}
func (p *Pack) ConnectGame(tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, p.ID}
_, err := tx.Exec(data.query, args...)
return err
}
func (p *Pack) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}
var dataId *string
if !strings.EqualFold(data.fieldName, "game_id") {
dataId = &data.id
}
args := []any{dataId, p.ID, p.Name, p.Label, p.Banner, p.Path, p.Type,
p.System, p.PackageType, p.PackageName}
res, err := tx.Exec(data.query, args...)
if err != nil {
return err
}
rowsAffected, err := res.RowsAffected()
if rowsAffected != 0 {
err = p.InsertObjects(tx)
if err != nil {
return err
}
}
if dataId == nil {
dataCopy := *data
p.ConnectGameQuery(&dataCopy)
err = p.ConnectGame(tx, &dataCopy)
}
return err
}
func (p *Pack) ConnectGameCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, p.ID}
_, err := tx.ExecContext(ctx, data.query, args...)
return err
}
func (p *Pack) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}
var dataId *string
if !strings.EqualFold(data.fieldName, "game_id") {
dataId = &data.id
}
args := []any{dataId, p.ID, p.Name, p.Label, p.Banner, p.Path, p.Type,
p.System, p.PackageType, p.PackageName}
res, err := tx.ExecContext(ctx, data.query, args...)
if err != nil {
return err
}
rowsAffected, err := res.RowsAffected()
if rowsAffected != 0 {
err = p.InsertObjects(tx)
if err != nil {
return err
}
}
if dataId == nil {
dataCopy := *data
p.ConnectGameQuery(&dataCopy)
err = p.ConnectGameCtx(ctx, tx, &dataCopy)
}
return err
}
type PackFolder struct {
ID string
Description string
Name string
Sorting string
Type string
Sort int
}
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[string]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, p.ID, p.Description, p.Name, p.Sorting, p.Type, p.Sort}
_, err := tx.Exec(data.query, args...)
if err != nil {
return err
}
return nil
}
func (p *PackFolder) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, p.ID, p.Description, p.Name, p.Sorting, p.Type, p.Sort}
_, err := tx.ExecContext(ctx, data.query, args...)
if err != nil {
return err
}
return nil
}

View File

@@ -0,0 +1,169 @@
package db
import (
"context"
"database/sql"
"errors"
"fmt"
"github.com/jmoiron/sqlx"
"golang.org/x/sync/errgroup"
)
type PackageWarning struct {
ID uint
Key string
Value *PackageWarningsData
}
func (p *PackageWarning) Query(data *InsertId[uint]) {
data.query = fmt.Sprintf(`
INSERT INTO package_warnings (%s, key_)
VALUES ($1, $2)
RETURNING id`, data.fieldName)
}
func (p *PackageWarning) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, p.Key}
err := tx.QueryRowx(data.query, args...).Scan(&p.ID)
if err != nil {
return err
}
InsertWithCtx(tx, p.Value, InsertId[uint]{id: p.ID})
return nil
}
func (p *PackageWarning) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, p.Key}
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&p.ID)
if err != nil {
return err
}
return InsertWithCtx(tx, p.Value, InsertId[uint]{id: p.ID})
}
type PackageWarningsData struct {
ID string
Type string
Manifest string
Reinstallable bool
Warning []string
Error []string
}
func (p *PackageWarningsData) InsertObjects(tx *sqlx.Tx) error {
group, _ := errgroup.WithContext(context.Background())
warningData := &InsertId[string]{id: p.ID, fieldName: "package_warnings_data_id", tableName: "package_warnings_data_warning"}
InsertSimpleSliceParallel(group, tx, p.Warning, warningData)
errorData := &InsertId[string]{id: p.ID, fieldName: "package_warnings_data_id", tableName: "package_warnings_data_error"}
InsertSimpleSliceParallel(group, tx, p.Warning, errorData)
err := group.Wait()
if err != nil && !errors.Is(err, sql.ErrNoRows) {
return err
}
return nil
}
func (p *PackageWarningsData) Query(data *InsertId[uint]) {
data.query = `
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 {
if data.query == "" {
return ErrNoQuery
}
args := []any{p.ID, p.Type, p.Reinstallable, p.Manifest}
res, err := tx.Exec(data.query, args...)
if err != nil {
return err
}
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 {
if data.query == "" {
return ErrNoQuery
}
args := []any{p.ID, p.Type, p.Reinstallable, p.Manifest}
res, err := tx.ExecContext(ctx, data.query, args...)
if err != nil {
return err
}
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

@@ -0,0 +1,145 @@
package db
import (
"context"
"database/sql"
"errors"
"github.com/jmoiron/sqlx"
"golang.org/x/sync/errgroup"
)
type Playlist struct {
ID string
Name string
Folder string
Sorting string
Description string
Channel string
Mode int
Fade int
Seed int
Sort int
Playing bool
Stats Stats
Ownership []OwnershipString
Sounds []*Sound
}
func (p *Playlist) Query(data *InsertId[uint]) {
data.query = `
INSERT INTO playlist (game_id, id, name, folder, sorting, description, channel, mode, fade, seed, sort, playing)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
ON CONFLICT(id) DO UPDATE SET
game_id = EXCLUDED.game_id,
updated_at = datetime('now')
RETURNING (created_at == updated_at) AS is_inserted`
}
func (p *Playlist) InsertObjects(tx *sqlx.Tx) error {
group, ctx := errgroup.WithContext(context.Background())
relId := InsertId[string]{id: p.ID, fieldName: "playlist_id"}
InsertWithCtxParallel(group, ctx, tx, p.Stats, relId)
InsertSliceParallel(group, tx, p.Ownership, relId)
InsertSliceParallel(group, tx, p.Sounds, relId)
err := group.Wait()
if err != nil && !errors.Is(err, sql.ErrNoRows) {
return err
}
return nil
}
func (p *Playlist) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, p.ID, p.Name, p.Folder, p.Sorting, p.Description, p.Channel, p.Mode, p.Fade, p.Seed, p.Sort, p.Playing}
var isInserted bool
err := tx.QueryRowx(data.query, args...).Scan(&isInserted)
if err != nil {
return err
}
if isInserted {
return p.InsertObjects(tx)
}
return nil
}
func (p *Playlist) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, p.ID, p.Name, p.Folder, p.Sorting, p.Description, p.Channel, p.Mode, p.Fade, p.Seed, p.Sort, p.Playing}
var isInserted bool
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&isInserted)
if err != nil {
return err
}
if isInserted {
return p.InsertObjects(tx)
}
return nil
}
type Sound struct {
ID string
Name string
Path string
Channel string
Description string
Fade int
Sort int
Repeat bool
Playing bool
Volume float64
PausedTime float64
}
func (s *Sound) Query(data *InsertId[string]) {
data.query = `
INSERT INTO sound (playlist_id, id, name, path, channel, description, fade, sort, repeat, playing, volume, paused_time)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
ON CONFLICT(id) DO UPDATE SET
playlist_id = EXCLUDED.playlist_id,
updated_at = datetime('now')`
}
func (s *Sound) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, s.ID, s.Name, s.Path, s.Channel, s.Description, s.Fade, s.Sort, s.Repeat, s.Playing, s.Volume, s.PausedTime}
_, err := tx.Exec(data.query, args...)
if err != nil {
return err
}
return nil
}
func (s *Sound) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, s.ID, s.Name, s.Path, s.Channel, s.Description, s.Fade, s.Sort, s.Repeat, s.Playing, s.Volume, s.PausedTime}
_, err := tx.ExecContext(ctx, data.query, args...)
if err != nil {
return err
}
return nil
}

View File

@@ -0,0 +1,131 @@
package db
import (
"context"
"database/sql"
"errors"
"fmt"
"strconv"
"github.com/jmoiron/sqlx"
"golang.org/x/sync/errgroup"
)
type Relationships struct {
ID uint
Systems []RelationshipsData
Requires []RelationshipsData
Recommends []RelationshipsData
Conflicts []RelationshipsData
}
func (r Relationships) Query(data *InsertId[string]) {
data.query = fmt.Sprintf(`
INSERT INTO relationships (%s)
VALUES ($1)
RETURNING id`, data.fieldName)
}
func (r Relationships) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id}
err := tx.QueryRowx(data.query, args...).Scan(&r.ID)
if err != nil {
return err
}
group, _ := errgroup.WithContext(context.Background())
InsertSliceParallel(group, tx, r.Systems, InsertId[uint]{id: r.ID, tableName: "relationships_systems"})
InsertSliceParallel(group, tx, r.Requires, InsertId[uint]{id: r.ID, tableName: "relationships_requires"})
InsertSliceParallel(group, tx, r.Recommends, InsertId[uint]{id: r.ID, tableName: "relationships_recommends"})
InsertSliceParallel(group, tx, r.Conflicts, InsertId[uint]{id: r.ID, tableName: "relationships_conflicts"})
err = group.Wait()
if err != nil && !errors.Is(err, sql.ErrNoRows) {
return err
}
return nil
}
func (r Relationships) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id}
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&r.ID)
if err != nil {
return err
}
group, _ := errgroup.WithContext(context.Background())
InsertSliceParallel(group, tx, r.Systems, InsertId[uint]{id: r.ID, tableName: "relationships_systems"})
InsertSliceParallel(group, tx, r.Requires, InsertId[uint]{id: r.ID, tableName: "relationships_requires"})
InsertSliceParallel(group, tx, r.Recommends, InsertId[uint]{id: r.ID, tableName: "relationships_recommends"})
InsertSliceParallel(group, tx, r.Conflicts, InsertId[uint]{id: r.ID, tableName: "relationships_conflicts"})
err = group.Wait()
if err != nil && !errors.Is(err, sql.ErrNoRows) {
return err
}
return nil
}
type RelationshipsData struct {
ID uint
Key string
Type string
Manifest string
Compatibility Compatibility
}
func (r RelationshipsData) Query(data *InsertId[uint]) {
data.query = fmt.Sprintf(`
INSERT INTO %s (relationships_id, key_, type, manifest)
VALUES ($1, $2, $3, $4)`, data.tableName)
}
func (r RelationshipsData) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, r.Key, r.Type, r.Manifest}
err := tx.QueryRowx(data.query, args...).Scan(&r.ID)
if err != nil {
return err
}
group, ctx := errgroup.WithContext(context.Background())
InsertWithCtxParallel(group, ctx, tx, r.Compatibility, InsertId[string]{id: strconv.FormatUint(uint64(r.ID), 10), fieldName: fmt.Sprintf("%s_id", data.tableName)})
return group.Wait()
}
func (r RelationshipsData) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, r.Key, r.Type, r.Manifest}
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&r.ID)
if err != nil {
return err
}
group, ctx := errgroup.WithContext(context.Background())
InsertWithCtxParallel(group, ctx, tx, r.Compatibility, InsertId[string]{id: strconv.FormatUint(uint64(r.ID), 10), fieldName: fmt.Sprintf("%s_id", data.tableName)})
return group.Wait()
}

View File

@@ -0,0 +1,61 @@
package db
import (
"context"
"fmt"
"github.com/jmoiron/sqlx"
)
type Release struct {
ID uint
Generation int
Build int
NodeVersion int
MaxGeneration int
MaxStableGeneration int
Time int64
Channel string
Suffix string
}
func (r *Release) Query(data *InsertId[uint]) {
data.query = fmt.Sprintf(`
INSERT INTO release_ (%s, generation, build, node_version, max_generation, max_stable_generation,
time, channel, suffix)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
RETURNING id`, data.fieldName)
}
func (r *Release) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, r.Generation, r.Build, r.NodeVersion, r.MaxGeneration, r.MaxStableGeneration,
r.Time, r.Channel, r.Suffix}
err := tx.QueryRowx(data.query, args...).Scan(&r.ID)
if err != nil {
return err
}
return nil
}
func (r *Release) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, r.Generation, r.Build, r.NodeVersion, r.MaxGeneration, r.MaxStableGeneration,
r.Time, r.Channel, r.Suffix}
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&r.ID)
if err != nil {
return err
}
return nil
}

View File

@@ -0,0 +1,159 @@
package db
import (
"context"
"database/sql"
"errors"
"fmt"
"github.com/jmoiron/sqlx"
"golang.org/x/sync/errgroup"
)
type Ring struct {
ID uint
Enabled bool
Effects int
RingColors RingColors
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
Ring string
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

@@ -0,0 +1,178 @@
package db
type Scene struct {
ID string
Folder string
Name string
Active bool
Navigation bool
NavOrder int
NavName string
Background SceneBackground
Foreground string
ForegroundElevation int
Thumb string
Width int
Height int
Padding float64
Initial SceneInitial
BackgroundColor string
Grid SceneGrid
TokenVision bool
Drawings []SceneDrawing
Tokens []*Token
Lights []SceneLight
Notes []Note
Sounds []ScenesSound
Walls []Wall
Playlist string
PlaylistSound string
Journal string
JournalEntryPage string
Weather string
Sort int
Ownership []OwnershipString
Stats Stats
Fog SceneFog
Environment Environment
}
type SceneBackground struct {
ID uint
Src string
ScaleX float64
ScaleY float64
OffsetX float64
OffsetY float64
Rotation int
Tint string
AnchorX float64
AnchorY float64
Fit string
AlphaThreshold int
}
type SceneInitial struct {
ID uint
X float64
Y float64
Scale float64
}
type SceneGrid struct {
ID uint
Type int
Size int
Color string
Alpha float64
Distance int
Units string
Style string
Thickness int
}
type SceneDrawing struct {
ID string
Author string
Shape SceneDrawingShape
X float64
Y float64
Rotation int
BezierFactor int
FillType int
FillColor string
FillAlpha float64
StrokeWidth int
StrokeColor string
StrokeAlpha int
Texture string
Text string
FontFamily string
FontSize int
TextColor string
TextAlpha int
Hidden bool
Locked bool
Interface bool
Elevation int
Sort int
}
type SceneDrawingShape struct {
ID uint
Type string `json:"type"`
Width int `json:"width"`
Height int `json:"height"`
}
type SceneLight struct {
ID string
X float64
Y float64
Rotation int
Walls bool
Vision bool
Config *Light
Hidden bool
Elevation int
}
type ScenesSound struct {
ID string
Path string
X float64
Y float64
Radius float64
Easing bool
Walls bool
Volume float64
Darkness ScenesSoundsDarkness
Repeat bool
Hidden bool
Elevation float64
Effects ScenesSoundsEffects
}
type ScenesSoundsDarkness struct {
ID uint
Min int
Max int
}
type ScenesSoundsEffects struct {
ID uint
Base ScenesSoundsEffectsBase
Muffled ScenesSoundsEffectsBase
}
type ScenesSoundsEffectsBase struct {
ID uint
Intensity int
}
type SceneFog struct {
ID uint
Exploration bool
Reset int64
Overlay string
Colors SceneFogColors
}
type SceneFogColors struct {
ID uint
Explored string
Unexplored string
}

View File

@@ -0,0 +1,79 @@
package db
import (
"context"
"database/sql"
"errors"
"github.com/jmoiron/sqlx"
"golang.org/x/sync/errgroup"
)
type Setting struct {
ID string
Key string
Value string
Stats Stats
}
func (s *Setting) Query(data *InsertId[uint]) {
data.query = `
INSERT INTO setting (game_id, id, key_, value)
VALUES ($1, $2, $3, $4)
ON CONFLICT(id) DO UPDATE SET
game_id = EXCLUDED.game_id,
updated_at = datetime('now')
RETURNING (created_at == updated_at) AS is_inserted`
}
func (s *Setting) InsertObjects(tx *sqlx.Tx) error {
group, ctx := errgroup.WithContext(context.Background())
relId := InsertId[string]{id: s.ID, fieldName: "setting_id"}
InsertWithCtxParallel(group, ctx, tx, s.Stats, relId)
err := group.Wait()
if err != nil && !errors.Is(err, sql.ErrNoRows) {
return err
}
return nil
}
func (s *Setting) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, s.ID, s.Key, s.Value}
var isInserted bool
err := tx.QueryRowx(data.query, args...).Scan(&isInserted)
if err != nil {
return err
}
if isInserted {
return s.InsertObjects(tx)
}
return nil
}
func (s *Setting) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, s.ID, s.Key, s.Value}
var isInserted bool
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&isInserted)
if err != nil {
return err
}
if isInserted {
return s.InsertObjects(tx)
}
return nil
}

View File

@@ -0,0 +1,194 @@
package db
import (
"context"
"database/sql"
"errors"
"strconv"
"time"
"github.com/jmoiron/sqlx"
"golang.org/x/sync/errgroup"
)
type Setup struct {
ID uint
CreatedAt time.Time
IsAdmin bool
IsSetup bool
CoreUpdate CoreUpdate
FeaturedContent FeaturedContent
Files Files
Options *SetupOptions
Release Release
Languages []*SetupLanguage
Modules []*Module
News []*News
PackageWarnings []*PackageWarning
Systems []*System
Worlds []*World
}
func (s *Setup) InsertObjects(tx *sqlx.Tx) error {
relData := InsertId[uint]{id: s.ID, fieldName: "setup_id"}
group, ctx := errgroup.WithContext(context.Background())
InsertWithCtxParallel(group, ctx, tx, &s.CoreUpdate, relData)
InsertWithCtxParallel(group, ctx, tx, &s.FeaturedContent, relData)
InsertWithCtxParallel(group, ctx, tx, &s.Files, relData)
InsertWithCtxParallel(group, ctx, tx, s.Options, relData)
InsertWithCtxParallel(group, ctx, tx, &s.Release, relData)
InsertSliceParallel(group, tx, s.Languages, relData)
InsertSliceParallel(group, tx, s.Modules, relData)
InsertSliceParallel(group, tx, s.News, relData)
InsertSliceParallel(group, tx, s.PackageWarnings, relData)
relDataString := InsertId[string]{
id: strconv.FormatUint(uint64(s.ID), 10),
fieldName: "setup_id",
}
InsertSliceParallel(group, tx, s.Systems, relDataString)
InsertSliceParallel(group, tx, s.Worlds, relDataString)
err := group.Wait()
if err != nil && !errors.Is(err, sql.ErrNoRows) {
return err
}
return nil
}
func (s *Setup) Insert(db *sqlx.DB) error {
tx := db.MustBegin()
defer tx.Rollback()
const query = `
INSERT INTO setup (is_admin, is_setup)
VALUES ($1, $2)
RETURNING id, created_at`
args := []any{s.IsAdmin, s.IsSetup}
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
err := tx.QueryRowxContext(ctx, query, args...).Scan(&s.ID, &s.CreatedAt)
if err != nil {
return err
}
err = s.InsertObjects(tx)
if err != nil {
return err
}
return tx.Commit()
}
type FeaturedContent struct {
ID uint
Title string
Caption string
URL string
Image string
}
func (f *FeaturedContent) Query(data *InsertId[uint]) {
data.query = `
INSERT INTO featured_content (setup_id, title, caption, url, image)
VALUES ($1, $2, $3, $4, $5)
RETURNING id`
}
func (f *FeaturedContent) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, f.Title, f.Caption, f.URL, f.Image}
err := tx.QueryRowx(data.query, args...).Scan(&f.ID)
if err != nil {
return err
}
return nil
}
func (f *FeaturedContent) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, f.Title, f.Caption, f.URL, f.Image}
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&f.ID)
if err != nil {
return err
}
return nil
}
type News struct {
ID uint
Title string
Caption string
URL string
Image string
}
func (n *News) Query(data *InsertId[uint]) {
data.query = `
INSERT INTO news (setup_id, title, caption, url, image)
VALUES ($1, $2, $3, $4, $5)
RETURNING id`
}
func (n *News) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, n.Title, n.Caption, n.URL, n.Image}
err := tx.QueryRowx(data.query, args...).Scan(&n.ID)
if err != nil {
return err
}
return nil
}
func (n *News) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, n.Title, n.Caption, n.URL, n.Image}
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&n.ID)
if err != nil {
return err
}
return nil
}
func HasSetup(db *sqlx.DB) (bool, error) {
const query = `
SELECT EXISTS(SELECT 1 FROM setup)`
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
var exists bool
err := db.GetContext(ctx, &exists, query)
if err != nil {
return false, err
}
return exists, nil
}

View File

@@ -0,0 +1,54 @@
package db
import (
"context"
"fmt"
"github.com/jmoiron/sqlx"
)
type Stats struct {
ID uint
CoreVersion string
SystemID string
SystemVersion string
LastModifiedBy string
ModifiedTime int64
}
func (s Stats) Query(data *InsertId[string]) {
data.query = fmt.Sprintf(`
INSERT INTO stats (%s, core_version, system_id, system_version, last_modified_by, modified_time)
VALUES ($1, $2, $3, $4, $5, $6)`, data.fieldName)
}
func (s Stats) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, s.CoreVersion, s.SystemID, s.SystemVersion, s.LastModifiedBy, s.ModifiedTime}
err := tx.QueryRowx(data.query, args...).Scan(&s.ID)
if err != nil {
return err
}
return nil
}
func (s Stats) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, s.CoreVersion, s.SystemID, s.SystemVersion, s.LastModifiedBy, s.ModifiedTime}
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&s.ID)
if err != nil {
return err
}
return nil
}

View File

@@ -0,0 +1,51 @@
package db
import (
"context"
"fmt"
"github.com/jmoiron/sqlx"
)
type Style struct {
ID uint
Src string
}
func (s *Style) Query(data *InsertId[string]) {
data.query = fmt.Sprintf(`
INSERT INTO style (%s, src)
VALUES ($1, $2)
RETURNING id`, data.fieldName)
}
func (s *Style) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, s.Src}
err := tx.QueryRowx(data.query, args...).Scan(&s.ID)
if err != nil {
return err
}
return nil
}
func (s *Style) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, s.Src}
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&s.ID)
if err != nil {
return err
}
return nil
}

View File

@@ -4,201 +4,186 @@ import (
"context"
"database/sql"
"errors"
"time"
"strings"
"github.com/jmoiron/sqlx"
"golang.org/x/sync/errgroup"
)
type System struct {
Id int64
TextId string
Title string
Description string
Url string
Download string
CreatedAt time.Time
Compatibility Compatibility
ID string
Title string
Description string
URL string
License string
Bugs string
Changelog string
Version string
Manifest string
Download string
Background string
PrimaryTokenAttribute string
Availability int
Socket bool
Protected bool
Exclusive bool
PersistentStorage bool
Locked bool
Owned bool
HasStorage bool
Compatibility Compatibility
Relationships Relationships
DocumentTypes DocumentTypes
Grid *Grid
Esmodules []string
Scripts []string
Tags []string
Authors []*Author
Media []*Media
Packs []*Pack
Styles []*Style
Languages []*Language
PackFolders []*Folder
}
type Systems []System
func (systems Systems) GetById(id int) *System {
return &systems[id]
func (s *System) Query(data *InsertId[string]) {
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)
ON CONFLICT(id) DO NOTHING`
}
func (m FoundryStateModel) InsertSystem(system *System, stateId int64) error {
query := `
INSERT INTO systems (state_id, text_id, title, description, url, download)
VALUES ($1, $2, $3, $4, $5, $6)
RETURNING id, created_at`
args := []any{stateId, system.TextId, system.Title, system.Description, system.Url, system.Download}
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
err := m.DB.QueryRowContext(ctx, query, args...).Scan(&system.Id, &system.CreatedAt)
if err != nil && err != sql.ErrNoRows {
return err
}
return m.InsertSystemCompatibility(&system.Compatibility, system.Id)
func (s *System) ConnectGameQuery(data *InsertId[string]) {
data.query = `
INSERT INTO game_to_systems (game_id, system_id)
VALUES ($1, $2)`
}
func (m FoundryStateModel) InsertSystemCompatibility(compatibility *Compatibility, systemId int64) error {
query := `
INSERT INTO systems_compatibility (system_id, minimum, verified, maximum)
VALUES ($1, $2, $3, $4)`
func (s *System) InsertObjects(tx *sqlx.Tx) error {
relId := InsertId[string]{id: s.ID, fieldName: "system_id"}
group, ctx := errgroup.WithContext(context.Background())
args := []any{systemId, compatibility.Minimum, compatibility.Verified, compatibility.Maximum}
InsertWithCtxParallel(group, ctx, tx, s.Compatibility, relId)
InsertWithCtxParallel(group, ctx, tx, s.Relationships, relId)
InsertWithCtxParallel(group, ctx, tx, s.DocumentTypes, relId)
InsertWithCtxParallel(group, ctx, tx, s.Grid, relId)
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
esModulesRelId := &InsertId[string]{id: s.ID, fieldName: "system_id", tableName: "es_modules"}
InsertSimpleSliceParallel(group, tx, s.Esmodules, esModulesRelId)
scriptRelId := &InsertId[string]{id: s.ID, fieldName: "system_id", tableName: "scripts"}
InsertSimpleSliceParallel(group, tx, s.Scripts, scriptRelId)
tagsRelId := &InsertId[string]{id: s.ID, fieldName: "system_id", tableName: "tags"}
InsertSimpleSliceParallel(group, tx, s.Tags, tagsRelId)
err := m.DB.QueryRowContext(ctx, query, args...).Scan(&compatibility.Id)
if err != nil && err != sql.ErrNoRows {
InsertSliceParallel(group, tx, s.Authors, relId)
InsertSliceParallel(group, tx, s.Media, relId)
InsertSliceParallel(group, tx, s.Styles, relId)
InsertSliceParallel(group, tx, s.Languages, relId)
InsertSliceParallel(group, tx, s.Packs, relId)
InsertSliceParallel(group, tx, s.PackFolders, relId)
err := group.Wait()
if err != nil && !errors.Is(err, sql.ErrNoRows) {
return err
}
return nil
}
func (m FoundryStateModel) GetSystems(idState int64) (Systems, error) {
if idState < 1 {
return nil, ErrorRecordNotFound
func (s *System) ConnectGame(tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}
query := `
SELECT id, created_at, text_id, title, description, url, download
FROM systems
WHERE state_id = $1`
args := []any{data.id, s.ID}
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
_, err := tx.Exec(data.query, args...)
return err
}
rows, err := m.DB.QueryContext(ctx, query, idState)
func (s *System) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}
var dataId *string
if strings.EqualFold(data.fieldName, "setup_id") {
dataId = &data.id
}
args := []any{dataId, 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}
res, err := tx.Exec(data.query, args...)
if err != nil {
switch {
case errors.Is(err, sql.ErrNoRows):
return nil, ErrorRecordNotFound
default:
return nil, err
}
return err
}
systems := make(Systems, 0, 1)
for rows.Next() {
var system System
err := rows.Scan(
&system.Id,
&system.CreatedAt,
&system.TextId,
&system.Title,
&system.Description,
&system.Url,
&system.Download,
)
rowsAffected, err := res.RowsAffected()
if rowsAffected != 0 {
err = s.InsertObjects(tx)
if err != nil {
return nil, err
return err
}
compatibility, err := m.GetModuleCompatibility(system.Id)
}
if dataId == nil {
dataCopy := *data
s.ConnectGameQuery(&dataCopy)
err = s.ConnectGame(tx, &dataCopy)
}
return err
}
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 {
if data.query == "" {
return ErrNoQuery
}
var dataId *string
if strings.EqualFold(data.fieldName, "setup_id") {
dataId = &data.id
}
args := []any{dataId, 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}
res, err := tx.ExecContext(ctx, data.query, args...)
if err != nil {
return err
}
rowsAffected, err := res.RowsAffected()
if rowsAffected != 0 {
err = s.InsertObjects(tx)
if err != nil {
return nil, err
}
system.Compatibility = *compatibility
systems = append(systems, system)
}
if err := rows.Err(); err != nil {
return nil, err
}
return systems, nil
}
func (m FoundryStateModel) GetSystemCompatibility(idSystem int64) (*Compatibility, error) {
if idSystem < 1 {
return nil, ErrorRecordNotFound
}
query := `
SELECT id, minimum, verified, maximum
FROM systems_compatibility
WHERE system_id = $1`
var compatibility Compatibility
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
err := m.DB.QueryRowContext(ctx, query, idSystem).Scan(
&compatibility.Id,
&compatibility.Minimum,
&compatibility.Verified,
&compatibility.Maximum,
)
if err != nil {
switch {
case errors.Is(err, sql.ErrNoRows):
return nil, ErrorRecordNotFound
default:
return nil, err
return err
}
}
return &compatibility, nil
}
func (m FoundryStateModel) DeleteSystems(idState int64) error {
if idState < 1 {
return ErrorRecordNotFound
}
query := `
DELETE FROM systems
WHERE state_id = $1`
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
result, err := m.DB.ExecContext(ctx, query, idState)
if err != nil {
return err
}
rowsAffected, err := result.RowsAffected()
if err != nil {
return err
}
if rowsAffected == 0 {
return ErrorRecordNotFound
}
return nil
}
func (m FoundryStateModel) DeleteSystem(idSystem int64) error {
if idSystem < 1 {
return ErrorRecordNotFound
}
query := `
DELETE FROM systems
WHERE id = $1`
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
result, err := m.DB.ExecContext(ctx, query, idSystem)
if err != nil {
return err
}
rowsAffected, err := result.RowsAffected()
if err != nil {
return err
}
if rowsAffected == 0 {
return ErrorRecordNotFound
}
return nil
if dataId == nil {
dataCopy := *data
s.ConnectGameQuery(&dataCopy)
err = s.ConnectGameCtx(ctx, tx, &dataCopy)
}
return err
}

View File

@@ -0,0 +1,163 @@
package db
import (
"context"
"database/sql"
"errors"
"github.com/jmoiron/sqlx"
"golang.org/x/sync/errgroup"
)
type Table struct {
ID string
Name string
Description string
Formula string
Img string
Folder string
Sort int
Replacement bool
DisplayRoll bool
Stats Stats
Ownership []OwnershipString
Results []*TableResult
}
func (t *Table) Query(data *InsertId[uint]) {
data.query = `
INSERT INTO table_ (game_id, id, name, description, formula, img, folder, sort, replacement, display_roll)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
ON CONFLICT(id) DO UPDATE SET
game_id = EXCLUDED.game_id,
updated_at = datetime('now')
RETURNING (created_at == updated_at) AS is_inserted`
}
func (t *Table) InsertObjects(tx *sqlx.Tx) error {
group, ctx := errgroup.WithContext(context.Background())
relId := InsertId[string]{id: t.ID, fieldName: "table_id"}
InsertWithCtxParallel(group, ctx, tx, t.Stats, relId)
InsertSliceParallel(group, tx, t.Ownership, relId)
InsertSliceParallel(group, tx, t.Results, relId)
err := group.Wait()
if err != nil && !errors.Is(err, sql.ErrNoRows) {
return err
}
return nil
}
func (t *Table) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, t.ID, t.Name, t.Description, t.Formula, t.Img, t.Folder, t.Sort, t.Replacement, t.DisplayRoll}
var isInserted bool
err := tx.QueryRowx(data.query, args...).Scan(&isInserted)
if err != nil {
return err
}
if isInserted {
return t.InsertObjects(tx)
}
return nil
}
func (t *Table) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, t.ID, t.Name, t.Description, t.Formula, t.Img, t.Folder, t.Sort, t.Replacement, t.DisplayRoll}
var isInserted bool
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&isInserted)
if err != nil {
return err
}
if isInserted {
return t.InsertObjects(tx)
}
return nil
}
type TableResult struct {
ID string
Type string
Img string
Description string
Name string
Weight int
Drawn bool
Stats Stats
Range []int
}
func (t *TableResult) Query(data *InsertId[string]) {
data.query = `
INSERT INTO table_result (table_id, id, type, img, description, name, weight, drawn)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
ON CONFLICT(id) DO UPDATE SET
table_id = EXCLUDED.table_id,
updated_at = datetime('now')
RETURNING (created_at == updated_at) AS is_inserted`
}
func (t *TableResult) InsertObjects(tx *sqlx.Tx) error {
group, ctx := errgroup.WithContext(context.Background())
InsertWithCtxParallel(group, ctx, tx, t.Stats, InsertId[string]{id: t.ID, fieldName: "table_result_id"})
InsertSimpleSlice(tx, t.Range, &InsertId[string]{id: t.ID, fieldName: "table_result_id", tableName: "table_result_range"})
err := group.Wait()
if err != nil && !errors.Is(err, sql.ErrNoRows) {
return err
}
return nil
}
func (t *TableResult) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, t.ID, t.Type, t.Img, t.Description, t.Name, t.Weight, t.Drawn}
var isInserted bool
err := tx.QueryRowx(data.query, args...).Scan(&isInserted)
if err != nil {
return err
}
if isInserted {
return t.InsertObjects(tx)
}
return nil
}
func (t *TableResult) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, t.ID, t.Type, t.Img, t.Description, t.Name, t.Weight, t.Drawn}
var isInserted bool
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&isInserted)
if err != nil {
return err
}
if isInserted {
return t.InsertObjects(tx)
}
return nil
}

View File

@@ -0,0 +1,331 @@
package db
import (
"context"
"database/sql"
"errors"
"fmt"
"github.com/jmoiron/sqlx"
"golang.org/x/sync/errgroup"
)
type Token struct {
ID uint
Name string
ActorLink bool
AppendNumber bool
PrependAdjective bool
LockRotation bool
RandomImg bool
DisplayName int
DisplayBars int
Disposition int
Rotation int
Alpha int
Width float64
Height float64
Ring *Ring
Sight *TokenSight
Texture *TokenTexture
Bar1 TokenBar
Bar2 TokenBar
Light *Light
Occludable TokenOccludable
TurnMarker TokenTurnMarker
}
func (t *Token) Query(data *InsertId[string]) {
data.query = `
INSERT INTO token (actor_id, 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`
}
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
Src string
Fit string
Tint string
ScaleX float64
ScaleY float64
OffsetX float64
OffsetY float64
Rotation float64
AnchorX float64
AnchorY float64
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
Color string
VisionMode string
Range int
Angle int
Attenuation float64
Brightness float64
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
Mode int
Animation string
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

@@ -0,0 +1,101 @@
package db
import (
"context"
"fmt"
"github.com/jmoiron/sqlx"
)
type CoreUpdate struct {
ID uint
HasUpdate bool
CanUpdate bool
CouldReachWebsite bool
SlowResponse bool
WillDisableModules bool
Version string
Channel string
}
func (c *CoreUpdate) Query(data *InsertId[uint]) {
data.query = fmt.Sprintf(`
INSERT INTO core_update (%s, has_update, can_update, could_reach_website, slow_response, will_disable_modules, version, channel)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
RETURNING id`, data.fieldName)
}
func (c *CoreUpdate) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, c.HasUpdate, c.CanUpdate, c.CouldReachWebsite, c.SlowResponse, c.WillDisableModules, c.Version, c.Channel}
err := tx.QueryRowx(data.query, args...).Scan(&c.ID)
if err != nil {
return err
}
return nil
}
func (c *CoreUpdate) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, c.HasUpdate, c.CanUpdate, c.CouldReachWebsite, c.SlowResponse, c.WillDisableModules, c.Version, c.Channel}
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&c.ID)
if err != nil {
return err
}
return nil
}
type SystemUpdate struct {
ID uint
HasUpdate bool
Version string
}
func (s *SystemUpdate) Query(data *InsertId[uint]) {
data.query = fmt.Sprintf(`
INSERT INTO system_update (%s, has_update, version)
VALUES ($1, $2, $3)
RETURNING id`, data.fieldName)
}
func (s *SystemUpdate) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, s.HasUpdate, s.Version}
err := tx.QueryRowx(data.query, args...).Scan(&s.ID)
if err != nil {
return err
}
return nil
}
func (s *SystemUpdate) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, s.HasUpdate, s.Version}
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&s.ID)
if err != nil {
return err
}
return nil
}

View File

@@ -4,293 +4,125 @@ import (
"context"
"database/sql"
"errors"
"time"
"github.com/jmoiron/sqlx"
"golang.org/x/sync/errgroup"
)
type User struct {
Id int64
ID string
Name string
Role int
Avatar string
Character string
Color string
Pronouns string
CreatedAt time.Time
Hotbar map[string]string
Stats UserStats
Role int
Stats Stats
Hotbar []UserHotbar
}
type UserStats struct {
Id int64
CoreVersion string
SystemId string
SystemVersion string
CreatedTime int64
ModifiedTime int64
LastModifiedBy string
func (u *User) Query(data *InsertId[uint]) {
data.query = `
INSERT INTO user (game_id, id, name, avatar, character, color, pronouns, role)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
ON CONFLICT(id) DO UPDATE SET
game_id = EXCLUDED.game_id,
updated_at = datetime('now')
RETURNING (created_at == updated_at) AS is_inserted`
}
type Users []User
func (u *User) InsertObjects(tx *sqlx.Tx) error {
group, ctx := errgroup.WithContext(context.Background())
func (users Users) GetById(id int) *User {
return &users[id]
relId := InsertId[string]{id: u.ID, fieldName: "user_id"}
InsertWithCtxParallel(group, ctx, tx, u.Stats, relId)
InsertSliceParallel(group, tx, u.Hotbar, relId)
err := group.Wait()
if err != nil && !errors.Is(err, sql.ErrNoRows) {
return err
}
return nil
}
func (m FoundryStateModel) InsertUser(user *User, stateId int64) error {
query := `
INSERT INTO users (state_id, name, role, character, color, pronouns)
VALUES ($1, $2, $3, $4, $5, $6)
RETURNING id, created_at`
func (u *User) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{stateId, user.Name, user.Role, user.Character, user.Color, user.Pronouns}
args := []any{data.id, u.ID, u.Name, u.Avatar, u.Character, u.Color, u.Pronouns, u.Role}
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
err := m.DB.QueryRowContext(ctx, query, args...).Scan(&user.Id, &user.CreatedAt)
var isInserted bool
err := tx.QueryRowx(data.query, args...).Scan(&isInserted)
if err != nil {
return err
}
for k, v := range user.Hotbar {
err = m.InsertUserHotbar(k, v, user.Id)
if err != nil {
return err
}
if isInserted {
return u.InsertObjects(tx)
}
return m.InsertUserStats(&user.Stats, user.Id)
return nil
}
func (m FoundryStateModel) InsertUserHotbar(key string, value string, userId int64) error {
query := `
INSERT INTO users_hotbar (user_id, key, value)
func (u *User) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, u.ID, u.Name, u.Avatar, u.Character, u.Color, u.Pronouns, u.Role}
var isInserted bool
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&isInserted)
if err != nil {
return err
}
if isInserted {
return u.InsertObjects(tx)
}
return nil
}
type UserHotbar struct {
ID uint
Key int
Value string
}
func (u UserHotbar) Query(data *InsertId[string]) {
data.query = `
INSERT INTO hotbar (user_id, key_, value)
VALUES ($1, $2, $3)`
args := []any{userId, key, value}
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
_, err := m.DB.ExecContext(ctx, query, args...)
if err != nil && err != sql.ErrNoRows {
return err
}
return nil
}
func (m FoundryStateModel) InsertUserStats(stats *UserStats, userId int64) error {
query := `
INSERT INTO users_stats (user_id, core_version, system_id, system_version, created_time, modified_time, last_modified_by)
VALUES ($1, $2, $3, $4, $5, $6, $7)
RETURNING id`
args := []any{userId, stats.CoreVersion, stats.SystemId, stats.SystemVersion, stats.CreatedTime, stats.ModifiedTime, stats.LastModifiedBy}
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
return m.DB.QueryRowContext(ctx, query, args...).Scan(&stats.Id)
}
func (m FoundryStateModel) GetUsers(idState int64) (Users, error) {
if idState < 1 {
return nil, ErrorRecordNotFound
func (u UserHotbar) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}
query := `
SELECT id, created_at, name, role, character, color, pronouns
FROM users
WHERE state_id = $1`
args := []any{data.id, u.Key, u.Value}
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
rows, err := m.DB.QueryContext(ctx, query, idState)
if err != nil {
switch {
case errors.Is(err, sql.ErrNoRows):
return nil, ErrorRecordNotFound
default:
return nil, err
}
}
users := make(Users, 0, 1)
for rows.Next() {
var user User
err := rows.Scan(
&user.Id,
&user.CreatedAt,
&user.Name,
&user.Role,
&user.Character,
&user.Color,
&user.Pronouns,
)
if err != nil {
return nil, err
}
user.Hotbar, err = m.GetUserHotbar(user.Id)
if err != nil {
return nil, err
}
userState, err := m.GetUserStats(user.Id)
if err != nil {
return nil, err
}
user.Stats = *userState
users = append(users, user)
}
if err := rows.Err(); err != nil {
return nil, err
}
return users, nil
}
func (m FoundryStateModel) GetUserHotbar(idUser int64) (map[string]string, error) {
if idUser < 1 {
return nil, ErrorRecordNotFound
}
query := `
SELECT key, value
FROM users_hotbar
WHERE user_id = $1`
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
rows, err := m.DB.QueryContext(ctx, query, idUser)
if err != nil {
switch {
case errors.Is(err, sql.ErrNoRows):
return nil, ErrorRecordNotFound
default:
return nil, err
}
}
hotbar := make(map[string]string)
for rows.Next() {
var key string
var val string
err := rows.Scan(
&key,
&val,
)
if err != nil {
return nil, err
}
hotbar[key] = val
}
if err != nil {
switch {
case errors.Is(err, sql.ErrNoRows):
return nil, ErrorRecordNotFound
default:
return nil, err
}
}
return hotbar, nil
}
func (m FoundryStateModel) GetUserStats(idSystem int64) (*UserStats, error) {
if idSystem < 1 {
return nil, ErrorRecordNotFound
}
query := `
SELECT id, core_version, system_id, system_version, created_time, modified_time, last_modified_by
FROM users_stats
WHERE user_id = $1`
var userStats UserStats
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
err := m.DB.QueryRowContext(ctx, query, idSystem).Scan(
&userStats.Id,
&userStats.CoreVersion,
&userStats.SystemId,
&userStats.SystemVersion,
&userStats.CreatedTime,
&userStats.ModifiedTime,
&userStats.LastModifiedBy,
)
if err != nil {
switch {
case errors.Is(err, sql.ErrNoRows):
return nil, ErrorRecordNotFound
default:
return nil, err
}
}
return &userStats, nil
}
func (m FoundryStateModel) DeleteUsers(idState int64) error {
if idState < 1 {
return ErrorRecordNotFound
}
query := `
DELETE FROM users
WHERE state_id = $1`
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
result, err := m.DB.ExecContext(ctx, query, idState)
err := tx.QueryRowx(data.query, args...).Scan(&u.ID)
if err != nil {
return err
}
rowsAffected, err := result.RowsAffected()
if err != nil {
return err
}
if rowsAffected == 0 {
return ErrorRecordNotFound
}
return nil
}
func (m FoundryStateModel) DeleteUser(idUser int64) error {
if idUser < 1 {
return ErrorRecordNotFound
func (u UserHotbar) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}
query := `
DELETE FROM users
WHERE id = $1`
args := []any{data.id, u.Key, u.Value}
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
result, err := m.DB.ExecContext(ctx, query, idUser)
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&u.ID)
if err != nil {
return err
}
rowsAffected, err := result.RowsAffected()
if err != nil {
return err
}
if rowsAffected == 0 {
return ErrorRecordNotFound
}
return nil
}

View File

@@ -0,0 +1,239 @@
package db
import (
"context"
"errors"
"fmt"
"time"
"github.com/jmoiron/sqlx"
"github.com/mattn/go-sqlite3"
"golang.org/x/sync/errgroup"
)
var (
ErrNoQuery = errors.New("Query has not been set")
ErrorRecordNotFound = errors.New("Record not found")
)
type AllowedIds interface {
~uint | ~string
}
type InsertId[T AllowedIds] struct {
id T
fieldName string
tableName string
query string
}
type Insertable[T AllowedIds] interface {
Query(data *InsertId[T])
Insert(tx *sqlx.Tx, relId *InsertId[T]) error
InsertCtx(ctx context.Context, tx *sqlx.Tx, relId *InsertId[T]) error
}
func InsertWithCtx[T AllowedIds, I Insertable[T]](tx *sqlx.Tx, data I, relId InsertId[T]) error {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
data.Query(&relId)
return data.InsertCtx(ctx, tx, &relId)
}
func InsertWithCtxParallel[T AllowedIds, I Insertable[T]](g *errgroup.Group, ctx context.Context, tx *sqlx.Tx, data I, relId InsertId[T]) {
g.Go(func() error {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
data.Query(&relId)
err := data.InsertCtx(ctx, tx, &relId)
return err
})
}
func InsertSlice[T AllowedIds, I Insertable[T]](tx *sqlx.Tx, data []I, relId InsertId[T]) error {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if len(data) > 0 {
data[0].Query(&relId)
}
for i := range data {
err := data[i].InsertCtx(ctx, tx, &relId)
if err != nil {
return err
}
}
return nil
}
func InsertSliceParallelTimeout[T AllowedIds, I Insertable[T]](g *errgroup.Group, tx *sqlx.Tx, data []I, relId InsertId[T], timeout time.Duration) {
g.Go(func() error {
wg, _ := errgroup.WithContext(context.Background())
var err error
if len(data) > 0 {
data[0].Query(&relId)
}
for i := range data {
wg.Go(func() error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
err = data[i].InsertCtx(ctx, tx, &relId)
return err
})
}
return wg.Wait()
})
}
func InsertSliceParallel[T AllowedIds, I Insertable[T]](g *errgroup.Group, tx *sqlx.Tx, data []I, relId InsertId[T]) {
g.Go(func() error {
wg, _ := errgroup.WithContext(context.Background())
var err error
if len(data) > 0 {
data[0].Query(&relId)
}
for i := range data {
wg.Go(func() error {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
err = data[i].InsertCtx(ctx, tx, &relId)
return err
})
}
return wg.Wait()
})
}
func InsertSimpleSlice[T AllowedIds, I any](tx *sqlx.Tx, data []I, relId *InsertId[T]) error {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
query := fmt.Sprintf(`
INSERT INTO %s (%s, value)
VALUES ($1, $2)`, relId.tableName, relId.fieldName)
for i := range data {
_, err := tx.ExecContext(ctx, query, relId.id, data[i])
if err != nil {
return err
}
}
return nil
}
func InsertSimpleSliceParallel[T AllowedIds, I any](g *errgroup.Group, tx *sqlx.Tx, data []I, relId *InsertId[T]) {
g.Go(func() error {
wg, _ := errgroup.WithContext(context.Background())
query := fmt.Sprintf(`
INSERT INTO %s (%s, value)
VALUES ($1, $2)`, relId.tableName, relId.fieldName)
for i := range data {
wg.Go(func() error {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
_, err := tx.ExecContext(ctx, query, relId.id, data[i])
return err
})
}
return wg.Wait()
})
}
func DeleteSetupAll(db *sqlx.DB) error {
const query = `DELETE FROM setup`
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
tx := db.MustBegin()
defer tx.Rollback()
result, err := tx.ExecContext(ctx, query)
if err != nil {
return err
}
tx.Commit()
rowsAffected, err := result.RowsAffected()
if err != nil {
return err
}
if rowsAffected == 0 {
return ErrorRecordNotFound
}
return nil
}
func DeleteGameAll(db *sqlx.DB) error {
const query = `DELETE FROM game`
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
tx := db.MustBegin()
defer tx.Rollback()
result, err := tx.ExecContext(ctx, query)
if err != nil {
return err
}
tx.Commit()
rowsAffected, err := result.RowsAffected()
if err != nil {
return err
}
if rowsAffected == 0 {
return ErrorRecordNotFound
}
return nil
}
func DeleteSeqAll(db *sqlx.DB) error {
const query = `DELETE FROM sqlite_sequence`
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
tx := db.MustBegin()
defer tx.Rollback()
result, err := tx.ExecContext(ctx, query)
if err != nil {
return err
}
tx.Commit()
rowsAffected, err := result.RowsAffected()
if err != nil {
return err
}
if rowsAffected == 0 {
return ErrorRecordNotFound
}
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 {
}
}
}

View File

@@ -0,0 +1,25 @@
package db
type Wall struct {
ID string
C []int
Light int
Move int
Sight int
Sound int
Dir int
Door int
Ds int
Threshold Threshold
Animation any
}
type Threshold struct {
ID uint
Light int
Sight int
Sound int
Attenuation bool
}

View File

@@ -4,231 +4,179 @@ import (
"context"
"database/sql"
"errors"
"fmt"
"time"
"github.com/jmoiron/sqlx"
"golang.org/x/sync/errgroup"
)
type World struct {
Id int64
TextId string
Title string
Description string
System string
CoreVersion string
SystemVersion string
LastPlayed string
PlayTime int64
NextSession time.Time
CreatedAt time.Time
Compatibility Compatibility
ID string
Title string
Description string
Version string
System string
Background string
JoinTheme string
CoreVersion string
SystemVersion string
LastPlayed string
Playtime int
Availability int
NextSession time.Time
Socket bool
Protected bool
Exclusive bool
PersistentStorage bool
Locked bool
Owned bool
HasStorage bool
Compatibility Compatibility
Relationships Relationships
Tags []string
Scripts []string
Esmodules []string
Authors []*Author
Media []*Media
Styles []*Style
Languages []*Language
Packs []*Pack
PackFolders []*Folder
}
type Worlds []World
func (worlds Worlds) GetById(id int) *World {
return &worlds[id]
func (w *World) Query(data *InsertId[string]) {
data.query = fmt.Sprintf(`
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)
ON CONFLICT(id) DO UPDATE SET
%[1]s = EXCLUDED.%[1]s,
updated_at = datetime('now')
RETURNING (created_at == updated_at) AS is_inserted`,
data.fieldName)
}
func (m FoundryStateModel) InsertWorld(world *World, stateId int64) error {
query := `
INSERT INTO worlds (state_id, text_id, title, description, system, core_version, system_version, playtime, next_session)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
RETURNING id, created_at`
func (w *World) InsertObjects(tx *sqlx.Tx) error {
relId := InsertId[string]{id: w.ID, fieldName: "world_id"}
group, ctx := errgroup.WithContext(context.Background())
args := []any{stateId, world.TextId, world.Title, world.Description, world.System, world.CoreVersion, world.SystemVersion, world.PlayTime, world.NextSession}
InsertWithCtxParallel(group, ctx, tx, w.Compatibility, relId)
InsertWithCtxParallel(group, ctx, tx, w.Relationships, relId)
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
esModulesRelId := &InsertId[string]{id: w.ID, fieldName: "world_id", tableName: "es_modules"}
InsertSimpleSliceParallel(group, tx, w.Esmodules, esModulesRelId)
scriptRelId := &InsertId[string]{id: w.ID, fieldName: "world_id", tableName: "scripts"}
InsertSimpleSliceParallel(group, tx, w.Scripts, scriptRelId)
tagsRelId := &InsertId[string]{id: w.ID, fieldName: "world_id", tableName: "tags"}
InsertSimpleSliceParallel(group, tx, w.Tags, tagsRelId)
err := m.DB.QueryRowContext(ctx, query, args...).Scan(&world.Id, &world.CreatedAt)
if err != nil && err != sql.ErrNoRows {
return err
}
return m.InsertWorldCompatibility(&world.Compatibility, world.Id)
}
InsertSliceParallel(group, tx, w.Authors, relId)
InsertSliceParallel(group, tx, w.Media, relId)
InsertSliceParallel(group, tx, w.Styles, relId)
InsertSliceParallel(group, tx, w.Languages, relId)
InsertSliceParallel(group, tx, w.Packs, relId)
InsertSliceParallel(group, tx, w.PackFolders, relId)
func (m FoundryStateModel) InsertWorldCompatibility(compatibility *Compatibility, worldId int64) error {
query := `
INSERT INTO worlds_compatibility (world_id, minimum, verified, maximum)
VALUES ($1, $2, $3, $4)
RETURNING id`
args := []any{worldId, compatibility.Minimum, compatibility.Verified, compatibility.Maximum}
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
err := m.DB.QueryRowContext(ctx, query, args...).Scan(&compatibility.Id)
if err != nil && err != sql.ErrNoRows {
err := group.Wait()
if err != nil && !errors.Is(err, sql.ErrNoRows) {
return err
}
return nil
}
func (m FoundryStateModel) GetWorlds(idState int64) (Worlds, error) {
if idState < 1 {
return nil, ErrorRecordNotFound
func (w *World) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}
query := `
SELECT id, created_at, text_id, title, description, system, core_version, system_version, playtime, next_session
FROM worlds
WHERE state_id = $1`
args := []any{data.id, w.ID, w.Title, w.Description, w.Version, w.System, w.Background, w.JoinTheme,
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}
var isInserted bool
err := tx.QueryRowx(data.query, args...).Scan(&isInserted)
if err != nil {
return err
}
if isInserted {
return w.InsertObjects(tx)
}
return nil
}
func (w *World) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error {
if data.query == "" {
return ErrNoQuery
}
args := []any{data.id, w.ID, w.Title, w.Description, w.Version, w.System, w.Background, w.JoinTheme,
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}
var isInserted bool
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&isInserted)
if err != nil {
return err
}
if isInserted {
return w.InsertObjects(tx)
}
return nil
}
func GetNotStartedWorlds(db *sqlx.DB) ([]string, error) {
const query = `
SELECT id FROM world WHERE game_id IS NULL`
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
rows, err := m.DB.QueryContext(ctx, query, idState)
var worldNames []string
err := db.SelectContext(ctx, &worldNames, query)
if err != nil {
switch {
case errors.Is(err, sql.ErrNoRows):
return nil, ErrorRecordNotFound
default:
return nil, err
}
}
worlds := make(Worlds, 0, 1)
for rows.Next() {
var world World
err := rows.Scan(
&world.Id,
&world.CreatedAt,
&world.TextId,
&world.Title,
&world.Description,
&world.System,
&world.CoreVersion,
&world.SystemVersion,
&world.PlayTime,
&world.NextSession,
)
if err != nil {
return nil, err
}
compatibility, err := m.GetWorldsCompatibility(world.Id)
if err != nil {
return nil, err
}
world.Compatibility = *compatibility
worlds = append(worlds, world)
}
if err := rows.Err(); err != nil {
return nil, err
}
return worlds, nil
return worldNames, nil
}
func (m FoundryStateModel) GetWorldsCompatibility(idWorld int64) (*Compatibility, error) {
if idWorld < 1 {
return nil, ErrorRecordNotFound
}
query := `
SELECT id, minimum, verified, maximum
FROM worlds_compatibility
WHERE world_id = $1`
var compatibility Compatibility
func IsWorldInserted(db *sqlx.DB, worldName string) (bool, error) {
const query = `
SELECT EXISTS(SELECT 1 FROM world WHERE id = $1 AND game_id IS NOT NULL)`
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
err := m.DB.QueryRowContext(ctx, query, idWorld).Scan(
&compatibility.Id,
&compatibility.Minimum,
&compatibility.Verified,
&compatibility.Maximum,
var exist bool
err := db.GetContext(ctx, &exist, query, worldName)
if err != nil {
return false, err
}
return exist, nil
}
func GetWorld(db *sqlx.DB, worldName string) (*World, error) {
const query = `
SELECT 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
FROM world WHERE id = $1`
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
var world World
err := db.QueryRowxContext(ctx, query, worldName).Scan(
&world.ID, &world.Title, &world.Description, &world.Version, &world.System, &world.Background, &world.JoinTheme,
&world.CoreVersion, &world.SystemVersion, &world.LastPlayed, &world.Playtime, &world.Availability, &world.NextSession,
&world.Socket, &world.Protected, &world.Exclusive, &world.PersistentStorage, &world.Locked, &world.Owned, &world.HasStorage,
)
if err != nil {
switch {
case errors.Is(err, sql.ErrNoRows):
return nil, ErrorRecordNotFound
default:
return nil, err
}
if err != nil && !errors.Is(err, sql.ErrNoRows) {
return nil, err
}
return &compatibility, nil
return &world, nil
}
func (m FoundryStateModel) DeleteWorlds(idState int64) error {
if idState < 1 {
return ErrorRecordNotFound
}
query := `
DELETE FROM worlds
WHERE state_id = $1`
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
result, err := m.DB.ExecContext(ctx, query, idState)
if err != nil {
return err
}
rowsAffected, err := result.RowsAffected()
if err != nil {
return err
}
if rowsAffected == 0 {
return ErrorRecordNotFound
}
return nil
}
func (m FoundryStateModel) DeleteWorld(idWorld int64) error {
if idWorld < 1 {
return ErrorRecordNotFound
}
query := `
DELETE FROM worlds
WHERE id = $1`
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
result, err := m.DB.ExecContext(ctx, query, idWorld)
if err != nil {
return err
}
rowsAffected, err := result.RowsAffected()
if err != nil {
return err
}
if rowsAffected == 0 {
return ErrorRecordNotFound
}
return nil
}
// func (worlds Worlds) GetSessionTime(worldName string) (*time.Time, error) {
// if worldName == "" && len(worlds) != 1 {
// return nil, ErrorSetupNotFound
// }
// if worldName == "" {
// return &worlds.GetById(0).NextSession, nil
// } else {
// for i := range worlds {
// if worlds[i].Id == worldName {
// return &worlds.GetById(0).NextSession, nil
// }
// }
// }
// return nil, ErrorSetupNotFound
// }
// func (world World) GetSessionTime() *time.Time {
// return &world.NextSession
// }

View File

@@ -0,0 +1,45 @@
package json
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
type Actor struct {
PrototypeToken Token `json:"prototypeToken"`
Img string `json:"img"`
Items []*Item `json:"items"`
Name string `json:"name"`
Type string `json:"type"`
Folder string `json:"folder"`
Ownership map[string]int `json:"ownership,omitempty"`
Stats Stats `json:"_stats"`
Sort int `json:"sort"`
ID string `json:"_id"`
// System any `json:"system,omitempty"`
// Flags any `json:"flags,omitempty"`
// ActorsEffects []any `json:"effects"`
}
func (a *Actor) ToDB(dest **db.Actor) bool {
if dest == nil {
return false
}
actor := &db.Actor{
Img: a.Img,
Name: a.Name,
Type: a.Type,
Folder: a.Folder,
Sort: a.Sort,
ID: a.ID,
}
a.PrototypeToken.ToDB(&actor.PrototypeToken)
a.Stats.ToDB(&actor.Stats)
OwnershipToDB(&actor.Ownership, a.Ownership)
CopySliceToDB(&actor.Items, a.Items)
*dest = actor
return true
}

View File

@@ -0,0 +1,21 @@
package json
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
type Addresses struct {
Local string `json:"local"`
Remote string `json:"remote"`
RemoteIsAccessible bool `json:"remoteIsAccessible"`
}
func (a *Addresses) ToDB(dest *db.Addresses) bool {
if dest == nil {
return false
}
dest.Local = a.Local
dest.Remote = a.Remote
dest.RemoteIsAccessible = a.RemoteIsAccessible
return true
}

View File

@@ -0,0 +1,28 @@
package json
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
type Author struct {
Name string `json:"name"`
URL string `json:"url"`
Email string `json:"email,omitempty"`
Discord string `json:"discord,omitempty"`
// SystemAuthorsFlags SystemAuthorsFlags `json:"flags"`
}
func (a *Author) ToDB(dest **db.Author) bool {
if dest == nil {
return false
}
author := &db.Author{
Name: a.Name,
URL: a.URL,
Email: a.Email,
Discord: a.Discord,
}
*dest = author
return true
}

View File

@@ -0,0 +1,138 @@
package json
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/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 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"`
}
func (c *CardDeck) ToDB(dest **db.CardDeck) bool {
if dest == nil {
return false
}
cardDeck := &db.CardDeck{
Name: c.Name,
Type: c.Type,
Description: c.Description,
Img: c.Img,
Width: c.Width,
Height: c.Height,
Rotation: c.Rotation,
DisplayCount: c.DisplayCount,
Folder: c.Folder,
Sort: c.Sort,
ID: c.ID,
}
c.Stats.ToDB(&cardDeck.Stats)
OwnershipToDB(&cardDeck.Ownership, c.Ownership)
CopySliceToDB(&cardDeck.Cards, c.Cards)
*dest = cardDeck
return true
}
type Card struct {
Name string `json:"name"`
Faces []*Face `json:"faces"`
Width int `json:"width"`
Height int `json:"height"`
Rotation int `json:"rotation"`
Type string `json:"type"`
Value int `json:"value"`
Suit string `json:"suit"`
Description string `json:"description"`
Face int `json:"face"`
Drawn bool `json:"drawn"`
Sort int `json:"sort"`
Back Back `json:"back"`
Origin string `json:"origin"`
ID string `json:"_id"`
Stats Stats `json:"_stats"`
// Flags any `json:"flags"`
// System any `json:"system"`
}
func (c *Card) ToDB(dest **db.Card) bool {
if dest == nil {
return false
}
card := &db.Card{
Name: c.Name,
Width: c.Width,
Height: c.Height,
Rotation: c.Rotation,
Type: c.Type,
Value: c.Value,
Suit: c.Suit,
Description: c.Description,
Face: c.Face,
Drawn: c.Drawn,
Origin: c.Origin,
ID: c.ID,
Sort: c.Sort,
}
c.Back.ToDB(&card.Back)
c.Stats.ToDB(&card.Stats)
CopySliceToDB(&card.Faces, c.Faces)
*dest = card
return true
}
type Face struct {
Name string `json:"name"`
Img string `json:"img"`
Text string `json:"text"`
}
func (f *Face) ToDB(dest *db.Face) bool {
if dest == nil {
return false
}
dest.Name = f.Name
dest.Img = f.Img
dest.Text = f.Text
return true
}
type Back struct {
Img any `json:"img"`
Name string `json:"name"`
Text string `json:"text"`
}
func (b *Back) ToDB(dest *db.Back) bool {
if dest == nil {
return false
}
dest.Name = b.Name
dest.Text = b.Text
return true
}

View File

@@ -0,0 +1,85 @@
package json
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
type Combat struct {
Id string `json:"_id"`
Type string `json:"type"`
Scene string `json:"scene"`
Groups []string `json:"groups"`
Combatants []*Combatant `json:"combatants"`
Active bool `json:"active"`
Round int `json:"round"`
Turn int `json:"turn"`
Sort int `json:"sort"`
Stats Stats `json:"stats"`
// System any `json:"system"`
// Flags any `json:"flags"`
}
func (c *Combat) ToDB(dest **db.Combat) bool {
if dest == nil {
return false
}
combat := &db.Combat{
ID: c.Id,
Type: c.Type,
Scene: c.Scene,
Active: c.Active,
Round: c.Round,
Turn: c.Turn,
Sort: c.Sort,
}
c.Stats.ToDB(&combat.Stats)
combat.Groups = make([]string, len(c.Groups))
copy(combat.Groups, c.Groups)
CopySliceToDB(&combat.Combatants, c.Combatants)
*dest = combat
return true
}
type Combatant struct {
TokenId string `json:"tokenId"`
SceneId string `json:"sceneId"`
ActorId string `json:"actorId"`
Hidden bool `json:"hidden"`
Id string `json:"_id"`
Type string `json:"type"`
Img string `json:"img"`
Initiative int `json:"initiative"`
Defeated bool `json:"defeated"`
Group string `json:"group"`
Stats Stats `json:"stats"`
// System any `json:"system"`
// Flags any `json:"flags"`
}
func (c *Combatant) ToDB(dest **db.Combatant) bool {
if dest == nil {
return false
}
combatant := &db.Combatant{
TokenId: c.TokenId,
SceneId: c.SceneId,
ActorId: c.ActorId,
Hidden: c.Hidden,
ID: c.Id,
Type: c.Type,
Img: c.Img,
Initiative: c.Initiative,
Defeated: c.Defeated,
Group: c.Group,
}
c.Stats.ToDB(&combatant.Stats)
*dest = combatant
return true
}

View File

@@ -0,0 +1,21 @@
package json
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
type Compatibility struct {
Minimum string `json:"minimum,omitempty"`
Verified string `json:"verified,omitempty"`
Maximum string `json:"maximum,omitempty"`
}
func (c *Compatibility) ToDB(dest *db.Compatibility) bool {
if dest == nil {
return false
}
dest.Minimum = c.Minimum
dest.Verified = c.Verified
dest.Maximum = c.Maximum
return true
}

View File

@@ -1,185 +0,0 @@
package json
import (
"encoding/json"
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
)
type FoundryState struct {
IsAdmin bool `json:"isAdmin,omitempty"`
IsSetup bool `json:"isSetup,omitempty"`
Languages []Language `json:"languages,omitempty"`
Modules []DataTemplate `json:"modules"`
Release Release `json:"release"`
Systems []DataTemplate `json:"systems,omitempty"`
Worlds []DataTemplate `json:"worlds,omitempty"`
World *DataTemplate `json:"world,omitempty"`
Users Users `json:"users,omitempty"`
Options Options `json:"options,omitempty"`
//coreUpdate struct{}
//featuredContent struct{}
//files struct{}
//news struct{}
//packageWarnings struct{} think about it
}
type Options struct {
Language string `json:"language,omitempty"`
}
func (state FoundryState) GetRelease() Release {
return state.Release
}
func (state FoundryState) GetFoundryStateDB(stateType db.StateType) *db.FoundryState {
dbFoundryState := db.FoundryState{
IsAdmin: state.IsAdmin,
IsSetup: state.IsSetup,
Type: stateType,
Options: db.Options{Language: state.Options.Language},
}
dbFoundryState.Modules = state.GetModules()
dbFoundryState.Systems = state.GetSystems()
dbFoundryState.Worlds = state.GetWorlds()
dbFoundryState.Users = state.GetUsers()
return &dbFoundryState
}
func (state *FoundryState) GetModules() db.Modules {
modulesCopy := make([]db.Module, 0, 8)
for i := range state.Modules {
module := &(state.Modules[i])
moduleCopy := db.Module{
TextId: module.Id,
Title: module.Title,
Description: module.Description,
Compatibility: db.Compatibility{
Minimum: module.Compatibility.Minimum,
Maximum: module.Compatibility.Maximum,
},
Url: module.Url,
Version: module.CoreVersion,
Availability: module.Availability,
}
for j := range module.Languages {
lang := db.Language{
Lang: module.Languages[j].Lang,
Name: module.Languages[j].Name,
Path: module.Languages[j].Path,
}
moduleCopy.Languages = append(moduleCopy.Languages, lang)
}
modulesCopy = append(modulesCopy, moduleCopy)
}
return modulesCopy
}
func (state *FoundryState) GetSystems() db.Systems {
systemsCopy := make([]db.System, 0, 8)
for i := range state.Systems {
system := &(state.Systems[i])
systemCopy := db.System{
TextId: system.Id,
Title: system.Title,
Description: system.Description,
Url: system.Url,
Compatibility: db.Compatibility{
Minimum: system.Compatibility.Minimum,
Maximum: system.Compatibility.Maximum,
},
Download: system.Download,
}
systemsCopy = append(systemsCopy, systemCopy)
}
return systemsCopy
}
func (state *FoundryState) GetWorld() *db.World {
return &db.World{
TextId: state.World.Id,
Title: state.World.Title,
Description: state.World.Description,
Compatibility: db.Compatibility{
Minimum: state.World.Compatibility.Minimum,
Maximum: state.World.Compatibility.Maximum,
},
System: state.World.System,
CoreVersion: state.World.CoreVersion,
SystemVersion: state.World.SystemVersion,
LastPlayed: state.World.LastPlayed,
PlayTime: state.World.PlayTime,
NextSession: state.World.NextSession,
}
}
func (state *FoundryState) GetWorlds() db.Worlds {
worldsCopy := make([]db.World, 0, 8)
for i := range state.Worlds {
world := &(state.Worlds[i])
worldCopy := db.World{
TextId: world.Id,
Title: world.Title,
Description: world.Description,
Compatibility: db.Compatibility{
Minimum: world.Compatibility.Minimum,
Maximum: world.Compatibility.Maximum,
},
System: world.System,
CoreVersion: world.CoreVersion,
SystemVersion: world.SystemVersion,
LastPlayed: world.LastPlayed,
PlayTime: world.PlayTime,
NextSession: world.NextSession,
}
worldsCopy = append(worldsCopy, worldCopy)
}
if state.World != nil {
worldsCopy = append(worldsCopy, *state.GetWorld())
}
return worldsCopy
}
func (state *FoundryState) GetUsers() db.Users {
usersCopy := make(db.Users, 0, 8)
for i := range state.Users {
user := &(state.Users[i])
userCopy := db.User{
Name: user.Name,
Role: user.Role,
Character: user.Character,
Color: user.Color,
Pronouns: user.Pronouns,
Hotbar: user.Hotbar,
Stats: db.UserStats{
CoreVersion: user.Stats.CoreVersion,
SystemId: user.Stats.SystemId,
SystemVersion: user.Stats.SystemVersion,
CreatedTime: user.Stats.CreatedTime,
ModifiedTime: user.Stats.ModifiedTime,
LastModifiedBy: user.Stats.LastModifiedBy,
},
}
usersCopy = append(usersCopy, userCopy)
}
return usersCopy
}
func ParseSetupModel(data []byte) (*FoundryState, error) {
var modelSetup []FoundryState
err := json.Unmarshal(data, &modelSetup)
if err != nil {
return nil, err
}
if len(modelSetup) > 1 {
return nil, ErrorSetupMoreThanOne
}
return &modelSetup[0], nil
}

View File

@@ -0,0 +1,17 @@
package json
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
type DetailsLanguages struct {
Details string `json:"details"`
}
func (d *DetailsLanguages) ToDB(dest *db.DetailsLanguages) bool {
if dest == nil {
return false
}
dest.Details = d.Details
return true
}

View File

@@ -0,0 +1,39 @@
package json
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
type DocumentTypes struct {
Actor DocumentTypeData `json:"Actor"`
Item DocumentTypeData `json:"Item"`
RegionBehavior RegionBehavior `json:"RegionBehavior,omitempty"`
}
func (d *DocumentTypes) ToDB(dest *db.DocumentTypes) bool {
if dest == nil {
return false
}
dest.Data = make([]*db.DocumentTypeData, 2)
dest.Data[0] = &db.DocumentTypeData{Type: "Actor"}
d.Actor.ToDB(dest.Data[0])
dest.Data[1] = &db.DocumentTypeData{Type: "Item"}
d.Item.ToDB(dest.Data[1])
return true
}
type DocumentTypeData struct {
HtmlFields []string `json:"htmlFields"`
}
func (d *DocumentTypeData) ToDB(dest *db.DocumentTypeData) bool {
if dest == nil {
return false
}
dest.HtmlFields = d.HtmlFields
return true
}

View File

@@ -0,0 +1,97 @@
package json
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
type Environment struct {
GlobalLight EnvironmentGlobalLight `json:"globalLight"`
DarknessLevel float64 `json:"darknessLevel"`
DarknessLock bool `json:"darknessLock"`
Cycle bool `json:"cycle"`
ScenesEnvironmentBase EnvironmentBase `json:"base"`
Dark EnvironmentBase `json:"dark"`
}
func (e *Environment) ToDB(dest *db.Environment) bool {
if dest == nil {
return false
}
e.GlobalLight.ToDB(&dest.GlobalLight)
dest.DarknessLevel = e.DarknessLevel
dest.DarknessLock = e.DarknessLock
dest.Cycle = e.Cycle
e.ScenesEnvironmentBase.ToDB(&dest.ScenesEnvironmentBase)
e.Dark.ToDB(&dest.Dark)
return true
}
type EnvironmentGlobalLight struct {
Enabled bool `json:"enabled"`
Darkness GlobalLightDarkness `json:"darkness"`
Alpha float64 `json:"alpha"`
Bright bool `json:"bright"`
Color string `json:"color"`
Coloration float64 `json:"coloration"`
Luminosity float64 `json:"luminosity"`
Saturation float64 `json:"saturation"`
Contrast float64 `json:"contrast"`
Shadows float64 `json:"shadows"`
}
func (e *EnvironmentGlobalLight) ToDB(dest *db.EnvironmentGlobalLight) bool {
if dest == nil {
return false
}
dest.Enabled = e.Enabled
e.Darkness.ToDB(&dest.Darkness)
dest.Alpha = e.Alpha
dest.Bright = e.Bright
dest.Color = e.Color
dest.Coloration = e.Coloration
dest.Luminosity = e.Luminosity
dest.Saturation = e.Saturation
dest.Contrast = e.Contrast
dest.Shadows = e.Shadows
return true
}
type EnvironmentBase struct {
Hue float64 `json:"hue"`
Intensity float64 `json:"intensity"`
Luminosity float64 `json:"luminosity"`
Saturation float64 `json:"saturation"`
Shadows float64 `json:"shadows"`
}
func (e *EnvironmentBase) ToDB(dest *db.EnvironmentBase) bool {
if dest == nil {
return false
}
dest.Hue = e.Hue
dest.Intensity = e.Intensity
dest.Luminosity = e.Luminosity
dest.Saturation = e.Saturation
dest.Shadows = e.Shadows
return true
}
type GlobalLightDarkness struct {
Max int `json:"max"`
Min int `json:"min"`
}
func (g *GlobalLightDarkness) ToDB(dest *db.GlobalLightDarkness) bool {
if dest == nil {
return false
}
dest.Max = g.Max
dest.Min = g.Min
return true
}

View File

@@ -1,8 +0,0 @@
package json
import "errors"
var (
ErrorSetupMoreThanOne = errors.New("Passed more than one setupData")
ErrorSetupNotFound = errors.New("Not found setup data from your request")
)

View File

@@ -0,0 +1,21 @@
package json
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
type Files struct {
Storages []string `json:"storages"`
S3 any `json:"s3"`
}
func (f *Files) ToDB(dest *db.Files) bool {
if dest == nil {
return false
}
dest.Storages = make([]db.FilesStorage, len(f.Storages))
for i := range f.Storages {
dest.Storages[i].Storage = f.Storages[i]
}
return true
}

View File

@@ -0,0 +1,67 @@
package json
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
type Folder struct {
Name string `json:"name"`
Sorting string `json:"sorting"`
Color string `json:"color,omitempty"`
Packs []string `json:"packs"`
Folders []*Folder `json:"folders,omitempty"`
}
func (f *Folder) ToDB(dest **db.Folder) bool {
if dest == nil {
return false
}
folder := &db.Folder{
Name: f.Name,
Sorting: f.Sorting,
Color: f.Color,
}
folder.Packs = make([]string, len(f.Packs))
copy(folder.Packs, f.Packs)
CopySliceToDB(&folder.Folders, f.Folders)
*dest = folder
return true
}
type WorldFolder struct {
Name string `json:"name"`
Type string `json:"type"`
ID string `json:"_id"`
Folder string `json:"folder"`
Sorting string `json:"sorting"`
Sort int `json:"sort"`
Stats Stats `json:"_stats,omitempty"`
Description string `json:"description"`
Color string `json:"color"`
// Flags any `json:"flags,omitempty"`
}
func (w *WorldFolder) ToDB(dest **db.WorldFolder) bool {
if dest == nil {
return false
}
worldFolder := &db.WorldFolder{
Name: w.Name,
Type: w.Type,
ID: w.ID,
Folder: w.Folder,
Sorting: w.Sorting,
Sort: w.Sort,
Description: w.Description,
Color: w.Color,
}
w.Stats.ToDB(&worldFolder.Stats)
*dest = worldFolder
return true
}

View File

@@ -0,0 +1,95 @@
package json
import (
"encoding/json"
"sync"
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
)
type Game struct {
UserID string `json:"userId"`
Release Release `json:"release"`
World World `json:"world"`
System System `json:"system"`
Modules []*Module `json:"modules"`
DemoMode bool `json:"demoMode"`
IdleLogout bool `json:"idleLogout"`
Addresses Addresses `json:"addresses"`
Files Files `json:"files"`
Options GameOptions `json:"options"`
ActiveUsers []string `json:"activeUsers"`
Paused bool `json:"paused"`
PackageWarnings map[string]PackageWarningsData `json:"packageWarnings"`
Packs []*Pack `json:"packs"`
Messages []*Message `json:"messages,omitempty"`
Combats []*Combat `json:"combats"`
CardDeck []*CardDeck `json:"cards"`
Users []*User `json:"users"`
Macros []*Macro `json:"macros"`
Folders []*WorldFolder `json:"folders"`
Items []*Item `json:"items"`
Settings []*Setting `json:"settings"`
Journals []*Journal `json:"journal"`
Tables []*Table `json:"tables"`
Playlists []*Playlist `json:"playlists"`
Scenes []*Scene `json:"scenes"`
Actors []*Actor `json:"actors"`
CoreUpdate CoreUpdate `json:"coreUpdate"`
SystemUpdate SystemUpdate `json:"systemUpdate"`
// Model Model `json:"model"`
// 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
dest.IdleLogout = g.IdleLogout
dest.Paused = g.Paused
PackageWarningsToDB(&dest.PackageWarnings, g.PackageWarnings)
g.Addresses.ToDB(&dest.Addresses)
g.Files.ToDB(&dest.Files)
g.Release.ToDB(&dest.Release)
g.World.ToDB(&dest.World)
g.System.ToDB(&dest.System)
g.Options.ToDB(&dest.Options)
g.CoreUpdate.ToDB(&dest.CoreUpdate)
g.SystemUpdate.ToDB(&dest.SystemUpdate)
dest.ActiveUsers = make([]string, len(g.ActiveUsers))
copy(dest.ActiveUsers, g.ActiveUsers)
wg := sync.WaitGroup{}
CopySliceToDBParallel(&wg, &dest.Modules, g.Modules)
CopySliceToDBParallel(&wg, &dest.Packs, g.Packs)
CopySliceToDBParallel(&wg, &dest.Messages, g.Messages)
CopySliceToDBParallel(&wg, &dest.Combats, g.Combats)
CopySliceToDBParallel(&wg, &dest.CardDeck, g.CardDeck)
CopySliceToDBParallel(&wg, &dest.Users, g.Users)
CopySliceToDBParallel(&wg, &dest.Macros, g.Macros)
CopySliceToDBParallel(&wg, &dest.Folders, g.Folders)
CopySliceToDBParallel(&wg, &dest.Items, g.Items)
CopySliceToDBParallel(&wg, &dest.Settings, g.Settings)
CopySliceToDBParallel(&wg, &dest.Journals, g.Journals)
CopySliceToDBParallel(&wg, &dest.Tables, g.Tables)
CopySliceToDBParallel(&wg, &dest.Playlists, g.Playlists)
CopySliceToDBParallel(&wg, &dest.Actors, g.Actors)
// go CopySliceToDBParallel(&wg, &dest.Scenes, g.Scenes)
wg.Wait()
return true
}

View File

@@ -0,0 +1,37 @@
package json
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
type Grid struct {
Type int `json:"type"`
Size int `json:"size,omitempty"`
Color string `json:"color,omitempty"`
Alpha float64 `json:"alpha,omitempty"`
Distance int `json:"distance"`
Units string `json:"units"`
Diagonals int `json:"diagonals,omitempty"`
Style string `json:"style,omitempty"`
Thickness int `json:"thickness,omitempty"`
}
func (g *Grid) ToDB(dest **db.Grid) bool {
if dest == nil {
return false
}
grid := &db.Grid{
Type: g.Type,
Size: g.Size,
Color: g.Color,
Alpha: g.Alpha,
Distance: g.Distance,
Units: g.Units,
Diagonals: g.Diagonals,
Style: g.Style,
Thickness: g.Thickness,
}
*dest = grid
return true
}

View File

@@ -0,0 +1,29 @@
package json
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
type Index struct {
Id string `json:"_id"`
Folder string `json:"folder"`
Img string `json:"img"`
Name string `json:"name"`
Type string `json:"type"`
}
func (i *Index) ToDB(dest **db.Index) bool {
if dest == nil {
return false
}
index := &db.Index{
ID: i.Id,
Folder: i.Folder,
Img: i.Img,
Name: i.Name,
Type: i.Type,
}
*dest = index
return true
}

View File

@@ -0,0 +1,38 @@
package json
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
type Item struct {
Img string `json:"img"`
Name string `json:"name"`
Type string `json:"type"`
Folder string `json:"folder"`
Ownership map[string]int `json:"ownership"`
Stats Stats `json:"_stats"`
ID string `json:"_id"`
Sort int `json:"sort"`
// System any `json:"system,omitempty"`
// Flags any `json:"flags,omitempty"`
// Effects []any `json:"effects"`
}
func (i *Item) ToDB(dest **db.Item) bool {
if dest == nil {
return false
}
item := &db.Item{
Img: i.Img,
Name: i.Name,
Type: i.Type,
Folder: i.Folder,
ID: i.ID,
Sort: i.Sort,
}
i.Stats.ToDB(&item.Stats)
*dest = item
return true
}

View File

@@ -0,0 +1,34 @@
package json
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
type Journal struct {
Folder any `json:"folder"`
Name string `json:"name"`
Pages []*JournalPage `json:"pages"`
Sort int `json:"sort"`
Ownership map[string]int `json:"ownership,omitempty"`
ID string `json:"_id"`
// JournalFlags any `json:"flags,omitempty"`
// JournalStats Stats `json:"_stats"`
// Categories []any `json:"categories"`
}
func (j *Journal) ToDB(dest **db.Journal) bool {
if dest == nil {
return false
}
journal := &db.Journal{
Name: j.Name,
Sort: j.Sort,
ID: j.ID,
}
OwnershipToDB(&journal.Ownership, j.Ownership)
CopySliceToDB(&journal.Pages, j.Pages)
*dest = journal
return true
}

View File

@@ -0,0 +1,95 @@
package json
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
type JournalPage struct {
Name string `json:"name"`
Type string `json:"type"`
Text PageText `json:"text,omitempty"`
ID string `json:"_id"`
Title PageTitle `json:"title"`
Video PageVideo `json:"video"`
Src string `json:"src"`
Sort int `json:"sort"`
Ownership map[string]int `json:"ownership"`
Stats Stats `json:"_stats,omitempty"`
// System PagesSystem `json:"system"`
// Image any `json:"image"`
// Flags any `json:"flags"`
// Category any `json:"category"`
}
func (j *JournalPage) ToDB(dest **db.JournalPage) bool {
if dest == nil {
return false
}
journalPage := &db.JournalPage{
Name: j.Name,
Type: j.Type,
ID: j.ID,
Src: j.Src,
Sort: j.Sort,
}
j.Text.ToDB(&journalPage.Text)
j.Title.ToDB(&journalPage.Title)
j.Video.ToDB(&journalPage.Video)
j.Stats.ToDB(&journalPage.Stats)
OwnershipToDB(&journalPage.Ownership, j.Ownership)
*dest = journalPage
return true
}
type PageText struct {
Content string `json:"content"`
Format int `json:"format"`
Markdown string `json:"markdown,omitempty"`
}
func (p *PageText) ToDB(dest *db.PageText) bool {
if dest == nil {
return false
}
dest.Content = p.Content
dest.Format = p.Format
dest.Markdown = p.Markdown
return true
}
type PageTitle struct {
Show bool `json:"show"`
Level int `json:"level"`
}
func (p *PageTitle) ToDB(dest *db.PageTitle) bool {
if dest == nil {
return false
}
dest.Show = p.Show
dest.Level = p.Level
return true
}
type PageVideo struct {
Controls bool `json:"controls"`
Volume float64 `json:"volume"`
}
func (p *PageVideo) ToDB(dest *db.PageVideo) bool {
if dest == nil {
return false
}
dest.Controls = p.Controls
dest.Volume = p.Volume
return true
}

View File

@@ -1,13 +1,67 @@
package json
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
type Language struct {
Id string `json:"id"`
Label string `json:"label"`
Modules []LangModule `json:"modules"`
Lang string `json:"lang"`
Name string `json:"name"`
Path string `json:"path"`
// SystemLanguagesFlags SystemLanguagesFlags `json:"flags"`
}
type LangModule struct {
Id string `json:"id"`
func (l *Language) ToDB(dest **db.Language) bool {
if dest == nil {
return false
}
lang := &db.Language{
Lang: l.Lang,
Name: l.Name,
Path: l.Path,
}
*dest = lang
return true
}
type SetupLanguage struct {
ID string `json:"id"`
Label string `json:"label"`
Modules []*SetupLanguageModule `json:"modules"`
}
func (s *SetupLanguage) ToDB(dest **db.SetupLanguage) bool {
if dest == nil {
return false
}
lang := &db.SetupLanguage{
ID: s.ID,
Label: s.Label,
}
CopySliceToDB(&lang.Modules, s.Modules)
*dest = lang
return true
}
type SetupLanguageModule struct {
ID string `json:"id"`
Label string `json:"label"`
Path string `json:"path"`
}
func (s *SetupLanguageModule) ToDB(dest *db.SetupLanguageModule) bool {
if dest == nil {
return false
}
dest.ID = s.ID
dest.Label = s.Label
dest.Path = s.Path
return true
}

View File

@@ -0,0 +1,85 @@
package json
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
type Light struct {
Alpha float64 `json:"alpha"`
Angle int `json:"angle"`
Bright float64 `json:"bright"`
Coloration float64 `json:"coloration"`
Dim float64 `json:"dim"`
Attenuation float64 `json:"attenuation"`
Luminosity float64 `json:"luminosity"`
Saturation float64 `json:"saturation"`
Contrast float64 `json:"contrast"`
Shadows float64 `json:"shadows"`
LightAnimation LightAnimation `json:"animation"`
LightDarkness LightDarkness `json:"darkness"`
Negative bool `json:"negative"`
Priority int `json:"priority"`
Color string `json:"color"`
}
func (l *Light) ToDB(dest **db.Light) bool {
if dest == nil {
return false
}
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(&light.LightAnimation)
l.LightDarkness.ToDB(&light.LightDarkness)
*dest = light
return true
}
type LightAnimation struct {
Type any `json:"type"`
Speed int `json:"speed"`
Intensity int `json:"intensity"`
Reverse bool `json:"reverse"`
}
func (l *LightAnimation) ToDB(dest *db.LightAnimation) bool {
if dest == nil {
return false
}
dest.Speed = l.Speed
dest.Intensity = l.Intensity
dest.Reverse = l.Reverse
return true
}
type LightDarkness struct {
Min float64 `json:"min"`
Max float64 `json:"max"`
}
func (l *LightDarkness) ToDB(dest *db.LightDarkness) bool {
if dest == nil {
return false
}
dest.Min = l.Min
dest.Max = l.Max
return true
}

View File

@@ -0,0 +1,44 @@
package json
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
type Macro struct {
Command string `json:"command"`
Name string `json:"name"`
Type string `json:"type"`
Img string `json:"img"`
ID string `json:"_id"`
Author string `json:"author"`
Scope string `json:"scope"`
Folder string `json:"folder"`
Sort int `json:"sort"`
Ownership map[string]int `json:"ownership,omitempty"`
Stats Stats `json:"_stats"`
// Flags any `json:"flags,omitempty"`
}
func (m *Macro) ToDB(dest **db.Macro) bool {
if dest == nil {
return false
}
macro := &db.Macro{
Command: m.Command,
Name: m.Name,
Type: m.Type,
Img: m.Img,
ID: m.ID,
Author: m.Author,
Scope: m.Scope,
Folder: m.Folder,
Sort: m.Sort,
}
m.Stats.ToDB(&macro.Stats)
OwnershipToDB(&macro.Ownership, m.Ownership)
*dest = macro
return true
}

View File

@@ -0,0 +1,25 @@
package json
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
type Media struct {
Type string `json:"type"`
URL string `json:"url"`
Caption string `json:"caption"`
}
func (m *Media) ToDB(dest **db.Media) bool {
if dest == nil {
return false
}
media := &db.Media{
Type: m.Type,
URL: m.URL,
Caption: m.Caption,
}
*dest = media
return true
}

View File

@@ -0,0 +1,73 @@
package json
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
type Message struct {
Content string `json:"content"`
Style int `json:"style"`
Author string `json:"author"`
Id string `json:"_id"`
Type string `json:"type"`
Timestamp int64 `json:"timestamp"`
Flavor string `json:"flavor"`
Speaker Speaker `json:"speaker"`
Whisper []string `json:"whisper"`
Blind bool `json:"blind"`
Rolls []string `json:"rolls"`
Sound string `json:"sound"`
Emote bool `json:"emote"`
Stats Stats `json:"_stats"`
// System any `json:"system"`
// Flags any `json:"flags"`
}
func (m *Message) ToDB(dest **db.Message) bool {
if dest == nil {
return false
}
message := &db.Message{
Content: m.Content,
Style: m.Style,
Author: m.Author,
ID: m.Id,
Type: m.Type,
Timestamp: m.Timestamp,
Flavor: m.Flavor,
Blind: m.Blind,
Sound: m.Sound,
Emote: m.Emote,
}
m.Speaker.ToDB(&message.Speaker)
m.Stats.ToDB(&message.Stats)
message.Whisper = make([]string, len(m.Whisper))
copy(message.Whisper, m.Whisper)
message.Rolls = make([]string, len(m.Rolls))
copy(message.Rolls, m.Rolls)
*dest = message
return true
}
type Speaker struct {
Scene string `json:"scene,omitempty"`
Actor string `json:"actor,omitempty"`
Token string `json:"token,omitempty"`
Alias string `json:"alias,omitempty"`
}
func (s *Speaker) ToDB(dest *db.Speaker) bool {
if dest == nil {
return false
}
dest.Scene = s.Scene
dest.Actor = s.Actor
dest.Token = s.Token
dest.Alias = s.Alias
return true
}

View File

@@ -0,0 +1,100 @@
package json
import (
"sync"
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
)
type Module struct {
ID string `json:"id"`
Title string `json:"title"`
Description string `json:"description"`
Authors []*Author `json:"authors"`
URL string `json:"url,omitempty"`
License string `json:"license,omitempty"`
Readme string `json:"readme,omitempty"`
Bugs string `json:"bugs,omitempty"`
Changelog string `json:"changelog,omitempty"`
Media []*Media `json:"media"`
Version string `json:"version"`
Compatibility Compatibility `json:"compatibility,omitempty"`
Scripts []string `json:"scripts"`
Esmodules []string `json:"esmodules"`
Styles []*Style `json:"styles"`
Languages []*Language `json:"languages"`
Packs []*Pack `json:"packs"`
PackFolders []*Folder `json:"packFolders"`
Relationships Relationships `json:"relationships"`
Socket bool `json:"socket"`
Manifest string `json:"manifest,omitempty"`
Download string `json:"download,omitempty"`
Protected bool `json:"protected"`
Exclusive bool `json:"exclusive"`
PersistentStorage bool `json:"persistentStorage"`
CoreTranslation bool `json:"coreTranslation"`
Library bool `json:"library"`
DocumentTypes DocumentTypes `json:"documentTypes"`
Availability int `json:"availability"`
Locked bool `json:"locked"`
Owned bool `json:"owned"`
Tags []string `json:"tags"`
HasStorage bool `json:"hasStorage"`
Active bool `json:"active,omitempty"`
// ModulesFlags ModulesFlags `json:"flags,omitempty"`
}
func (m *Module) ToDB(dest **db.Module) bool {
if dest == nil {
return false
}
module := &db.Module{
ID: m.ID,
Title: m.Title,
Description: m.Description,
URL: m.URL,
License: m.License,
Readme: m.Readme,
Bugs: m.Bugs,
Changelog: m.Changelog,
Version: m.Version,
Socket: m.Socket,
Manifest: m.Manifest,
Download: m.Download,
Protected: m.Protected,
Exclusive: m.Exclusive,
PersistentStorage: m.PersistentStorage,
CoreTranslation: m.CoreTranslation,
Library: m.Library,
Availability: m.Availability,
Locked: m.Locked,
Owned: m.Owned,
HasStorage: m.HasStorage,
Active: m.Active,
}
module.Scripts = make([]string, len(m.Scripts))
copy(module.Scripts, m.Scripts)
module.Esmodules = make([]string, len(m.Esmodules))
copy(module.Esmodules, m.Esmodules)
module.Tags = make([]string, len(m.Tags))
copy(module.Tags, m.Tags)
m.Compatibility.ToDB(&module.Compatibility)
m.Relationships.ToDB(&module.Relationships)
m.DocumentTypes.ToDB(&module.DocumentTypes)
wg := sync.WaitGroup{}
CopySliceToDBParallel(&wg, &module.Authors, m.Authors)
CopySliceToDBParallel(&wg, &module.Media, m.Media)
CopySliceToDBParallel(&wg, &module.Styles, m.Styles)
CopySliceToDBParallel(&wg, &module.Languages, m.Languages)
CopySliceToDBParallel(&wg, &module.Packs, m.Packs)
CopySliceToDBParallel(&wg, &module.PackFolders, m.PackFolders)
wg.Wait()
*dest = module
return true
}

View File

@@ -1,59 +0,0 @@
package modules
type DiceStats struct {
PlayerRollData DiceStatsRollData `json:"player_roll_data"`
}
type DiceStatsRollData struct {
PlayerDice []DicePlayerDice `json:"PLAYER_DICE"`
Username string `json:"USERNAME"`
Userid string `json:"USERID"`
Gm bool `json:"GM"`
PlayerRollInfo DiceRollInfo `json:"PLAYER_ROLL_INFO"`
}
type DicePlayerDice struct {
Type string `json:"TYPE"`
Max int `json:"MAX"`
TotalRolls int `json:"TOTAL_ROLLS"`
Rolls []int `json:"ROLLS"`
BlindRolls []int `json:"BLIND_ROLLS"`
StreakSize int `json:"STREAK_SIZE"`
StreakInit int `json:"STREAK_INIT"`
StreakIsBlind bool `json:"STREAK_ISBLIND"`
LongestStreak int `json:"LONGEST_STREAK"`
LongestStreakInit int `json:"LONGEST_STREAK_INIT"`
Mean int `json:"MEAN"`
Median int `json:"MEDIAN"`
Mode int `json:"MODE"`
Means []int `json:"MEANS"`
Medians []int `json:"MEDIANS"`
Modes []int `json:"MODES"`
RollCounters []int `json:"ROLL_COUNTERS"`
AtkRolls []int `json:"ATK_ROLLS"`
DmgRolls []int `json:"DMG_ROLLS"`
SavesRolls []int `json:"SAVES_ROLLS"`
SkillsRolls []int `json:"SKILLS_ROLLS"`
AbilityRolls []int `json:"ABILITY_ROLLS"`
UnknownRolls []int `json:"UNKNOWN_ROLLS"`
PerceptionRolls []int `json:"PERCEPTION_ROLLS"`
InitiativeRolls []int `json:"INITIATIVE_ROLLS"`
AtkRollsBlind []int `json:"ATK_ROLLS_BLIND"`
DmgRollsBlind []int `json:"DMG_ROLLS_BLIND"`
SavesRollsBlind []int `json:"SAVES_ROLLS_BLIND"`
SkillsRollsBlind []int `json:"SKILLS_ROLLS_BLIND"`
AbilityRollsBlind []int `json:"ABILITY_ROLLS_BLIND"`
UnknownRollsBlind []int `json:"UNKNOWN_ROLLS_BLIND"`
PerceptionRollsBlind []int `json:"PERCEPTION_ROLLS_BLIND"`
InitiativeRollsBlind []int `json:"INITIATIVE_ROLLS_BLIND"`
}
type DiceRollInfo struct {
IsRollInfoTracked bool `json:"IS_ROLL_INFO_TRACKED"`
AtkOutcomeTracker []int `json:"ATK_OUTCOME_TRACKER"`
NumUntargetedAtks int `json:"NUM_UNTARGETED_ATKS"`
TotalAttacks int `json:"TOTAL_ATTACKS"`
SaveOutcomeTracker []int `json:"SAVE_OUTCOME_TRACKER"`
NumUntargetedSaves int `json:"NUM_UNTARGETED_SAVES"`
TotalSaves int `json:"TOTAL_SAVES"`
}

View File

@@ -1,12 +0,0 @@
package modules
type Pf2eModule struct {
settings Pf2eSettings
}
type Pf2eSettings struct {
showEffectPanel bool
showCheckDialogs bool
showDamageDialogs bool
monochromeDarkvision bool
}

View File

@@ -0,0 +1,81 @@
package json
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
type Note struct {
EntryID string `json:"entryId"`
PageID string `json:"pageId"`
Text string `json:"text"`
X float64 `json:"x"`
Y float64 `json:"y"`
Global bool `json:"global"`
IconSize int `json:"iconSize"`
Texture NoteTexture `json:"texture"`
FontFamily string `json:"fontFamily"`
FontSize int `json:"fontSize"`
TextColor string `json:"textColor"`
TextAnchor int `json:"textAnchor"`
ID string `json:"_id"`
Elevation int `json:"elevation"`
Sort int `json:"sort"`
// NotesFlags any `json:"flags"`
}
func (n *Note) ToDB(dest *db.Note) bool {
if dest == nil {
return false
}
dest.EntryID = n.EntryID
dest.PageID = n.PageID
dest.Text = n.Text
dest.X = n.X
dest.Y = n.Y
dest.Global = n.Global
dest.IconSize = n.IconSize
dest.FontFamily = n.FontFamily
dest.FontSize = n.FontSize
dest.TextColor = n.TextColor
dest.TextAnchor = n.TextAnchor
dest.ID = n.ID
dest.Elevation = n.Elevation
dest.Sort = n.Sort
n.Texture.ToDB(&dest.Texture)
return true
}
type NoteTexture struct {
Tint string `json:"tint"`
Src string `json:"src"`
ScaleX int `json:"scaleX"`
ScaleY int `json:"scaleY"`
OffsetX float64 `json:"offsetX"`
OffsetY float64 `json:"offsetY"`
Rotation int `json:"rotation"`
AnchorX float64 `json:"anchorX"`
AnchorY float64 `json:"anchorY"`
Fit string `json:"fit"`
AlphaThreshold int `json:"alphaThreshold"`
}
func (n *NoteTexture) ToDB(dest *db.NoteTexture) bool {
if dest == nil {
return false
}
dest.Tint = n.Tint
dest.Src = n.Src
dest.ScaleX = n.ScaleX
dest.ScaleY = n.ScaleY
dest.OffsetX = n.OffsetX
dest.OffsetY = n.OffsetY
dest.Rotation = n.Rotation
dest.AnchorX = n.AnchorX
dest.AnchorY = n.AnchorY
dest.Fit = n.Fit
dest.AlphaThreshold = n.AlphaThreshold
return true
}

View File

@@ -0,0 +1,80 @@
package json
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
type GameOptions struct {
Language string `json:"language"`
Port int `json:"port"`
RoutePrefix any `json:"routePrefix"`
UpdateChannel string `json:"updateChannel"`
}
func (g *GameOptions) ToDB(dest *db.GameOptions) bool {
if dest == nil {
return false
}
dest.Language = g.Language
dest.Port = g.Port
dest.UpdateChannel = g.UpdateChannel
return true
}
type SetupOptions struct {
AwsConfig any `json:"awsConfig"`
CompressSocket bool `json:"compressSocket"`
CompressStatic bool `json:"compressStatic"`
CSSTheme string `json:"cssTheme"`
DataPath string `json:"dataPath"`
Fullscreen bool `json:"fullscreen"`
Hostname string `json:"hostname"`
HotReload bool `json:"hotReload"`
Language string `json:"language"`
LocalHostname string `json:"localHostname"`
Port int `json:"port"`
ProxySSL bool `json:"proxySSL"`
Telemetry bool `json:"telemetry"`
UpdateChannel string `json:"updateChannel"`
Upnp bool `json:"upnp"`
DeleteNEDB bool `json:"deleteNEDB"`
NoBackups bool `json:"noBackups"`
// PasswordSalt any `json:"passwordSalt"`
// Protocol any `json:"protocol"`
// ProxyPort any `json:"proxyPort"`
// RoutePrefix any `json:"routePrefix"`
// SslCert any `json:"sslCert"`
// SslKey any `json:"sslKey"`
// UpnpLeaseDuration any `json:"upnpLeaseDuration"`
// World any `json:"world"`
// AdminPassword string `json:"adminPassword"`
}
func (s *SetupOptions) ToDB(dest **db.SetupOptions) bool {
if dest == nil {
return false
}
options := &db.SetupOptions{
CompressSocket: s.CompressSocket,
CompressStatic: s.CompressStatic,
CSSTheme: s.CSSTheme,
DataPath: s.DataPath,
Fullscreen: s.Fullscreen,
Hostname: s.Hostname,
HotReload: s.HotReload,
Language: s.Language,
LocalHostname: s.LocalHostname,
Port: s.Port,
ProxySSL: s.ProxySSL,
Telemetry: s.Telemetry,
UpdateChannel: s.UpdateChannel,
Upnp: s.Upnp,
DeleteNEDB: s.DeleteNEDB,
NoBackups: s.NoBackups,
}
*dest = options
return true
}

View File

@@ -0,0 +1,15 @@
package json
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
type Ownership struct {
Player string `json:"PLAYER,omitempty"`
Trusted string `json:"TRUSTED,omitempty"`
Assistant string `json:"ASSISTANT,omitempty"`
}
func (o *Ownership) ToDB(dest *db.Ownership) {
dest.Player = o.Player
dest.Trusted = o.Trusted
dest.Assistant = o.Assistant
}

View File

@@ -0,0 +1,83 @@
package json
import (
"sync"
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
)
type Pack struct {
Name string `json:"name"`
Label string `json:"label"`
Banner string `json:"banner"`
Path string `json:"path"`
Type string `json:"type"`
System string `json:"system"`
Ownership Ownership `json:"ownership"`
PackageType string `json:"packageType,omitempty"`
PackageName string `json:"packageName,omitempty"`
Id string `json:"id,omitempty"`
Index []*Index `json:"index"`
Folders []*PackFolder `json:"folders"`
// SystemPacksFlags SystemPacksFlags `json:"flags"`
}
func (p *Pack) ToDB(dest **db.Pack) bool {
if dest == nil {
return false
}
pack := &db.Pack{
Name: p.Name,
Label: p.Label,
Banner: p.Banner,
Path: p.Path,
Type: p.Type,
System: p.System,
PackageType: p.PackageType,
PackageName: p.PackageName,
ID: p.Id,
}
p.Ownership.ToDB(&pack.Ownership)
wg := sync.WaitGroup{}
CopySliceToDBParallel(&wg, &pack.Index, p.Index)
CopySliceToDBParallel(&wg, &pack.Folders, p.Folders)
wg.Wait()
*dest = pack
return true
}
type PackFolder struct {
ID string `json:"_id"`
Color any `json:"color"`
Description string `json:"description"`
Folder any `json:"folder"`
Name string `json:"name"`
Sort int `json:"sort"`
Sorting string `json:"sorting"`
Type string `json:"type"`
// Packs0FoldersFlags any `json:"flags"`
}
func (p *PackFolder) ToDB(dest **db.PackFolder) bool {
if dest == nil {
return false
}
packFolder := &db.PackFolder{
ID: p.ID,
Description: p.Description,
Name: p.Name,
Sort: p.Sort,
Sorting: p.Sorting,
Type: p.Type,
}
*dest = packFolder
return true
}

View File

@@ -0,0 +1,35 @@
package json
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
type PackageWarningsData struct {
Id string `json:"id"`
Type string `json:"type"`
Warning []string `json:"warning"`
Error []string `json:"error"`
Reinstallable bool `json:"reinstallable"`
Manifest string `json:"manifest,omitempty"`
}
func (p *PackageWarningsData) ToDB(dest **db.PackageWarningsData) bool {
if dest == nil {
return false
}
packageData := &db.PackageWarningsData{
ID: p.Id,
Type: p.Type,
Reinstallable: p.Reinstallable,
Manifest: p.Manifest,
}
packageData.Warning = make([]string, len(p.Warning))
copy(packageData.Warning, p.Warning)
packageData.Error = make([]string, len(p.Error))
copy(packageData.Error, p.Error)
*dest = packageData
return true
}

View File

@@ -0,0 +1,90 @@
package json
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
type Playlist struct {
Name string `json:"name"`
ID string `json:"_id"`
Sounds []*Sound `json:"sounds"`
Mode int `json:"mode"`
Playing bool `json:"playing"`
Fade int `json:"fade,omitempty"`
Folder string `json:"folder"`
Sorting string `json:"sorting"`
Seed int `json:"seed,omitempty"`
Sort int `json:"sort"`
Ownership map[string]int `json:"ownership,omitempty"`
Stats Stats `json:"_stats"`
Description string `json:"description,omitempty"`
Channel string `json:"channel"`
// Flags any `json:"flags,omitempty"`
}
func (p *Playlist) ToDB(dest **db.Playlist) bool {
if dest == nil {
return false
}
playlist := &db.Playlist{
Name: p.Name,
ID: p.ID,
Mode: p.Mode,
Playing: p.Playing,
Fade: p.Fade,
Folder: p.Folder,
Sorting: p.Sorting,
Seed: p.Seed,
Sort: p.Sort,
Description: p.Description,
Channel: p.Channel,
}
p.Stats.ToDB(&playlist.Stats)
OwnershipToDB(&playlist.Ownership, p.Ownership)
CopySliceToDB(&playlist.Sounds, p.Sounds)
*dest = playlist
return true
}
type Sound struct {
Name string `json:"name"`
Path string `json:"path"`
ID string `json:"_id"`
Playing bool `json:"playing"`
PausedTime float64 `json:"pausedTime"`
Repeat bool `json:"repeat"`
Volume float64 `json:"volume"`
Fade int `json:"fade"`
Sort int `json:"sort"`
Channel string `json:"channel"`
Description string `json:"description,omitempty"`
// Flags any `json:"flags"`
}
func (s *Sound) ToDB(dest **db.Sound) bool {
if dest == nil {
return false
}
sound := &db.Sound{
Name: s.Name,
Path: s.Path,
ID: s.ID,
Playing: s.Playing,
PausedTime: s.PausedTime,
Repeat: s.Repeat,
Volume: s.Volume,
Fade: s.Fade,
Sort: s.Sort,
Channel: s.Channel,
Description: s.Description,
}
*dest = sound
return true
}

View File

@@ -0,0 +1,15 @@
package json
type RegionBehavior struct {
AdjustDarknessLevel any `json:"adjustDarknessLevel,omitempty"`
DisplayScrollingText any `json:"displayScrollingText,omitempty"`
ExecuteMacro any `json:"executeMacro,omitempty"`
ExecuteScript any `json:"executeScript,omitempty"`
ModifyMovementCost any `json:"modifyMovementCost,omitempty"`
PauseGame any `json:"pauseGame,omitempty"`
SuppressWeather any `json:"suppressWeather,omitempty"`
TeleportToken any `json:"teleportToken,omitempty"`
ToggleBehavior any `json:"toggleBehavior,omitempty"`
RegionBehaviorEnvironment any `json:"environment,omitempty"`
RegionBehaviorEnvironmentFeature any `json:"environmentFeature,omitempty"`
}

View File

@@ -0,0 +1,51 @@
package json
import (
"sync"
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
)
type Relationships struct {
Systems []*RelationshipsData `json:"systems,omitempty"`
Requires []*RelationshipsData `json:"requires,omitempty"`
Recommends []*RelationshipsData `json:"recommends,omitempty"`
Conflicts []*RelationshipsData `json:"conflicts,omitempty"`
// RelationshipsFlags RelationshipsFlags `json:"flags"`
}
func (r *Relationships) ToDB(dest *db.Relationships) bool {
if dest == nil {
return false
}
wg := sync.WaitGroup{}
CopySliceToDBParallel(&wg, &dest.Systems, r.Systems)
CopySliceToDBParallel(&wg, &dest.Requires, r.Requires)
CopySliceToDBParallel(&wg, &dest.Recommends, r.Recommends)
CopySliceToDBParallel(&wg, &dest.Conflicts, r.Conflicts)
wg.Wait()
return true
}
type RelationshipsData struct {
Id string `json:"id"`
Type string `json:"type"`
Manifest string `json:"manifest"`
Compatibility Compatibility `json:"compatibility"`
}
func (r *RelationshipsData) ToDB(dest *db.RelationshipsData) bool {
if dest == nil {
return false
}
dest.Key = r.Id
dest.Type = r.Type
dest.Manifest = r.Manifest
r.Compatibility.ToDB(&dest.Compatibility)
return true
}

View File

@@ -1,11 +1,32 @@
package json
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
type Release struct {
Generation int `json:"generation"`
Channel string `json:"channel"`
Suffix string `json:"suffix"`
Build int `json:"build"`
Node_version int `json:"node_version"`
Time int64 `json:"time"`
flags struct{} `json:"-"`
Generation int `json:"generation"`
Channel string `json:"channel"`
Suffix string `json:"suffix"`
Build int `json:"build"`
NodeVersion int `json:"node_version"`
MaxGeneration int `json:"maxGeneration"`
MaxStableGeneration int `json:"maxStableGeneration"`
Time int64 `json:"time"`
// Flags Flags `json:"flags"`
}
func (r *Release) ToDB(dest *db.Release) bool {
if dest == nil {
return false
}
dest.Generation = r.Generation
dest.Channel = r.Channel
dest.Suffix = r.Suffix
dest.Build = r.Build
dest.NodeVersion = r.NodeVersion
dest.MaxGeneration = r.MaxGeneration
dest.MaxStableGeneration = r.MaxStableGeneration
dest.Time = r.Time
return true
}

View File

@@ -0,0 +1,60 @@
package json
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
type Ring struct {
Enabled bool `json:"enabled"`
RingColors RingColors `json:"colors"`
Effects int `json:"effects"`
Subject Subject `json:"subject"`
}
func (r *Ring) ToDB(dest **db.Ring) bool {
if dest == nil {
return false
}
ring := &db.Ring{
Enabled: r.Enabled,
Effects: r.Effects,
}
r.RingColors.ToDB(&ring.RingColors)
r.Subject.ToDB(&ring.Subject)
*dest = ring
return true
}
type RingColors struct {
Ring string `json:"ring"`
Background string `json:"background"`
}
func (r *RingColors) ToDB(dest *db.RingColors) bool {
if dest == nil {
return false
}
dest.Ring = r.Ring
dest.Background = r.Background
return true
}
type Subject struct {
Scale int `json:"scale"`
Texture string `json:"texture"`
}
func (s *Subject) ToDB(dest *db.Subject) bool {
if dest == nil {
return false
}
dest.Scale = s.Scale
dest.Texture = s.Texture
return true
}

View File

@@ -0,0 +1,414 @@
package json
import (
"sync"
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
)
type Scene struct {
Folder string `json:"folder"`
Name string `json:"name"`
Active bool `json:"active"`
Navigation bool `json:"navigation"`
NavOrder int `json:"navOrder"`
NavName string `json:"navName"`
Background SceneBackground `json:"background"`
Foreground string `json:"foreground"`
ForegroundElevation int `json:"foregroundElevation"`
Thumb string `json:"thumb"`
Width int `json:"width"`
Height int `json:"height"`
Padding float64 `json:"padding"`
Initial SceneInitial `json:"initial"`
BackgroundColor string `json:"backgroundColor"`
Grid SceneGrid `json:"grid"`
TokenVision bool `json:"tokenVision"`
Drawings []*SceneDrawing `json:"drawings"`
Tokens []*Token `json:"tokens"`
Lights []*SceneLight `json:"lights"`
Notes []*Note `json:"notes"`
Sounds []*ScenesSound `json:"sounds"`
Walls []*Wall `json:"walls"`
Playlist string `json:"playlist"`
PlaylistSound string `json:"playlistSound"`
Journal string `json:"journal"`
JournalEntryPage string `json:"journalEntryPage"`
Weather string `json:"weather"`
Sort int `json:"sort"`
Ownership map[string]int `json:"ownership,omitempty"`
Stats Stats `json:"_stats"`
ID string `json:"_id"`
Fog SceneFog `json:"fog,omitempty"`
Environment Environment `json:"environment"`
// ScenesTemplates []any `json:"templates"`
// Tiles []any `json:"tiles"`
// ScenesFlags any `json:"flags,omitempty"`
// Regions []any `json:"regions"`
}
func (s *Scene) ToDB(dest *db.Scene) bool {
if dest == nil {
return false
}
dest.Folder = s.Folder
dest.Name = s.Name
dest.Active = s.Active
dest.Navigation = s.Navigation
dest.NavOrder = s.NavOrder
dest.NavName = s.NavName
dest.Foreground = s.Foreground
dest.ForegroundElevation = s.ForegroundElevation
dest.Thumb = s.Thumb
dest.Width = s.Width
dest.Height = s.Height
dest.Padding = s.Padding
dest.BackgroundColor = s.BackgroundColor
dest.TokenVision = s.TokenVision
dest.Playlist = s.Playlist
dest.PlaylistSound = s.PlaylistSound
dest.Journal = s.Journal
dest.JournalEntryPage = s.JournalEntryPage
dest.Weather = s.Weather
dest.Sort = s.Sort
dest.ID = s.ID
s.Background.ToDB(&dest.Background)
s.Initial.ToDB(&dest.Initial)
s.Grid.ToDB(&dest.Grid)
s.Stats.ToDB(&dest.Stats)
s.Fog.ToDB(&dest.Fog)
s.Environment.ToDB(&dest.Environment)
OwnershipToDB(&dest.Ownership, s.Ownership)
wg := sync.WaitGroup{}
CopySliceToDBParallel(&wg, &dest.Drawings, s.Drawings)
CopySliceToDBParallel(&wg, &dest.Tokens, s.Tokens)
CopySliceToDBParallel(&wg, &dest.Lights, s.Lights)
CopySliceToDBParallel(&wg, &dest.Notes, s.Notes)
CopySliceToDBParallel(&wg, &dest.Sounds, s.Sounds)
CopySliceToDBParallel(&wg, &dest.Walls, s.Walls)
wg.Wait()
return true
}
type SceneBackground struct {
Src string `json:"src"`
ScaleX float64 `json:"scaleX"`
ScaleY float64 `json:"scaleY"`
OffsetX float64 `json:"offsetX"`
OffsetY float64 `json:"offsetY"`
Rotation int `json:"rotation"`
Tint string `json:"tint"`
AnchorX float64 `json:"anchorX"`
AnchorY float64 `json:"anchorY"`
Fit string `json:"fit"`
AlphaThreshold int `json:"alphaThreshold"`
}
func (s *SceneBackground) ToDB(dest *db.SceneBackground) bool {
if dest == nil {
return false
}
dest.Src = s.Src
dest.ScaleX = s.ScaleX
dest.ScaleY = s.ScaleY
dest.OffsetX = s.OffsetX
dest.OffsetY = s.OffsetY
dest.Rotation = s.Rotation
dest.Tint = s.Tint
dest.AnchorX = s.AnchorX
dest.AnchorX = s.AnchorY
dest.Fit = s.Fit
dest.AlphaThreshold = s.AlphaThreshold
return true
}
type SceneInitial struct {
X float64 `json:"x"`
Y float64 `json:"y"`
Scale float64 `json:"scale"`
}
func (s *SceneInitial) ToDB(dest *db.SceneInitial) bool {
if dest == nil {
return false
}
dest.X = s.X
dest.Y = s.Y
dest.Scale = s.Scale
return true
}
type SceneGrid struct {
Type int `json:"type"`
Size int `json:"size"`
Color string `json:"color"`
Alpha float64 `json:"alpha"`
Distance int `json:"distance"`
Units string `json:"units"`
Style string `json:"style"`
Thickness int `json:"thickness"`
}
func (s *SceneGrid) ToDB(dest *db.SceneGrid) bool {
if dest == nil {
return false
}
dest.Type = s.Type
dest.Size = s.Size
dest.Color = s.Color
dest.Alpha = s.Alpha
dest.Distance = s.Distance
dest.Units = s.Units
dest.Style = s.Style
dest.Thickness = s.Thickness
return true
}
type SceneDrawing struct {
Author string `json:"author"`
Shape SceneDrawingShape `json:"shape"`
X float64 `json:"x"`
Y float64 `json:"y"`
Rotation int `json:"rotation"`
BezierFactor int `json:"bezierFactor"`
FillType int `json:"fillType"`
FillColor string `json:"fillColor"`
FillAlpha float64 `json:"fillAlpha"`
StrokeWidth int `json:"strokeWidth"`
StrokeColor string `json:"strokeColor"`
StrokeAlpha int `json:"strokeAlpha"`
Texture string `json:"texture"`
Text string `json:"text"`
FontFamily string `json:"fontFamily"`
FontSize int `json:"fontSize"`
TextColor string `json:"textColor"`
TextAlpha int `json:"textAlpha"`
Hidden bool `json:"hidden"`
Locked bool `json:"locked"`
ID string `json:"_id"`
Interface bool `json:"interface"`
Elevation int `json:"elevation"`
Sort int `json:"sort"`
// Flags any `json:"flags"`
}
func (s *SceneDrawing) ToDB(dest *db.SceneDrawing) bool {
if dest == nil {
return false
}
dest.Author = s.Author
dest.X = s.X
dest.Y = s.Y
dest.Rotation = s.Rotation
dest.BezierFactor = s.BezierFactor
dest.FillType = s.FillType
dest.FillColor = s.FillColor
dest.FillAlpha = s.FillAlpha
dest.StrokeWidth = s.StrokeWidth
dest.StrokeColor = s.StrokeColor
dest.StrokeAlpha = s.StrokeAlpha
dest.Texture = s.Texture
dest.Text = s.Text
dest.FontFamily = s.FontFamily
dest.FontSize = s.FontSize
dest.TextColor = s.TextColor
dest.TextAlpha = s.TextAlpha
dest.Hidden = s.Hidden
dest.Locked = s.Locked
dest.ID = s.ID
dest.Interface = s.Interface
dest.Elevation = s.Elevation
dest.Sort = s.Sort
s.Shape.ToDB(&dest.Shape)
return true
}
type SceneDrawingShape struct {
Type string `json:"type"`
Width int `json:"width"`
Height int `json:"height"`
Radius any `json:"radius"`
Points []any `json:"points"`
}
func (s *SceneDrawingShape) ToDB(dest *db.SceneDrawingShape) bool {
if dest == nil {
return false
}
dest.Type = s.Type
dest.Width = s.Width
dest.Height = s.Height
return true
}
type SceneLight struct {
X float64 `json:"x"`
Y float64 `json:"y"`
Rotation int `json:"rotation"`
Walls bool `json:"walls"`
Vision bool `json:"vision"`
Config Light `json:"config"`
Hidden bool `json:"hidden"`
Flags any `json:"flags"`
Id string `json:"_id"`
Elevation int `json:"elevation"`
}
func (s *SceneLight) ToDB(dest *db.SceneLight) bool {
if dest == nil {
return false
}
dest.X = s.X
dest.Y = s.Y
dest.Rotation = s.Rotation
dest.Walls = s.Walls
dest.Vision = s.Vision
dest.Hidden = s.Hidden
dest.ID = s.Id
dest.Elevation = s.Elevation
s.Config.ToDB(&dest.Config)
return true
}
type ScenesSound struct {
Path string `json:"path"`
X float64 `json:"x"`
Y float64 `json:"y"`
Radius float64 `json:"radius"`
Easing bool `json:"easing"`
Walls bool `json:"walls"`
Volume float64 `json:"volume"`
Darkness ScenesSoundsDarkness `json:"darkness"`
ID string `json:"_id"`
Repeat bool `json:"repeat"`
Hidden bool `json:"hidden"`
Elevation float64 `json:"elevation"`
Effects ScenesSoundsEffects `json:"effects"`
// Flags any `json:"flags"`
}
func (s *ScenesSound) ToDB(dest *db.ScenesSound) bool {
if dest == nil {
return false
}
dest.Path = s.Path
dest.X = s.X
dest.Y = s.Y
dest.Radius = s.Radius
dest.Easing = s.Easing
dest.Walls = s.Walls
dest.Volume = s.Volume
dest.ID = s.ID
dest.Repeat = s.Repeat
dest.Hidden = s.Hidden
dest.Elevation = s.Elevation
s.Darkness.ToDB(&dest.Darkness)
s.Effects.ToDB(&dest.Effects)
return true
}
type ScenesSoundsDarkness struct {
Min int `json:"min"`
Max int `json:"max"`
}
func (s *ScenesSoundsDarkness) ToDB(dest *db.ScenesSoundsDarkness) bool {
if dest == nil {
return false
}
dest.Min = s.Min
dest.Max = s.Max
return true
}
type ScenesSoundsEffects struct {
Base ScenesSoundsEffectsBase `json:"base"`
Muffled ScenesSoundsEffectsBase `json:"muffled"`
}
func (s *ScenesSoundsEffects) ToDB(dest *db.ScenesSoundsEffects) bool {
if dest == nil {
return false
}
s.Base.ToDB(&dest.Base)
s.Muffled.ToDB(&dest.Muffled)
return true
}
type ScenesSoundsEffectsBase struct {
Intensity int `json:"intensity"`
}
func (s *ScenesSoundsEffectsBase) ToDB(dest *db.ScenesSoundsEffectsBase) bool {
if dest == nil {
return false
}
dest.Intensity = s.Intensity
return true
}
type SceneFog struct {
Exploration bool `json:"exploration"`
Reset int64 `json:"reset"`
Overlay string `json:"overlay"`
Colors SceneFogColors `json:"colors"`
}
func (s *SceneFog) ToDB(dest *db.SceneFog) bool {
if dest == nil {
return false
}
dest.Exploration = s.Exploration
dest.Reset = s.Reset
dest.Overlay = s.Overlay
s.Colors.ToDB(&dest.Colors)
return true
}
type SceneFogColors struct {
Explored string `json:"explored"`
Unexplored string `json:"unexplored"`
}
func (s *SceneFogColors) ToDB(dest *db.SceneFogColors) bool {
if dest == nil {
return false
}
dest.Explored = s.Explored
dest.Unexplored = s.Unexplored
return true
}

View File

@@ -0,0 +1,29 @@
package json
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
type Setting struct {
Key string `json:"key"`
User any `json:"user"`
Value string `json:"value"`
ID string `json:"_id"`
Stats Stats `json:"_stats"`
}
func (s *Setting) ToDB(dest **db.Setting) bool {
if dest == nil {
return false
}
setting := &db.Setting{
Key: s.Key,
Value: s.Value,
ID: s.ID,
}
s.Stats.ToDB(&setting.Stats)
*dest = setting
return true
}

View File

@@ -0,0 +1,108 @@
package json
import (
"encoding/json"
"sync"
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
)
type Setup struct {
CoreUpdate CoreUpdate `json:"coreUpdate"`
FeaturedContent FeaturedContent `json:"featuredContent"`
Files Files `json:"files"`
IsAdmin bool `json:"isAdmin"`
IsSetup bool `json:"isSetup"`
Languages []*SetupLanguage `json:"languages"`
Modules []*Module `json:"modules"`
News []*News `json:"news"`
Options SetupOptions `json:"options"`
PackageWarnings map[string]PackageWarningsData `json:"packageWarnings"`
Release Release `json:"release"`
Systems []*System `json:"systems"`
Worlds []*World `json:"worlds"`
}
func ParseSetup(data []byte) (*Setup, error) {
var modelSetup []Setup
err := json.Unmarshal(data, &modelSetup)
if err != nil {
return nil, err
}
if len(modelSetup) > 1 {
return nil, ErrorSetupMoreThanOne
}
return &modelSetup[0], nil
}
func (s *Setup) ToDB(dest *db.Setup) bool {
if dest == nil {
return false
}
dest.IsAdmin = s.IsAdmin
dest.IsSetup = s.IsSetup
s.CoreUpdate.ToDB(&dest.CoreUpdate)
s.FeaturedContent.ToDB(&dest.FeaturedContent)
s.Files.ToDB(&dest.Files)
s.Options.ToDB(&dest.Options)
s.Release.ToDB(&dest.Release)
PackageWarningsToDB(&dest.PackageWarnings, s.PackageWarnings)
wg := sync.WaitGroup{}
CopySliceToDBParallel(&wg, &dest.Languages, s.Languages)
CopySliceToDBParallel(&wg, &dest.Modules, s.Modules)
CopySliceToDBParallel(&wg, &dest.News, s.News)
CopySliceToDBParallel(&wg, &dest.Systems, s.Systems)
CopySliceToDBParallel(&wg, &dest.Worlds, s.Worlds)
wg.Wait()
return true
}
type FeaturedContent struct {
Title string `json:"title"`
Caption string `json:"caption"`
URL string `json:"url"`
Image string `json:"image"`
}
func (f *FeaturedContent) ToDB(dest *db.FeaturedContent) bool {
if dest == nil {
return false
}
dest.Title = f.Title
dest.Caption = f.Caption
dest.URL = f.URL
dest.Image = f.Image
return true
}
type News struct {
Title string `json:"title"`
Caption string `json:"caption"`
URL string `json:"url"`
Image string `json:"image"`
}
func (n *News) ToDB(dest **db.News) bool {
if dest == nil {
return false
}
news := &db.News{
Title: n.Title,
Caption: n.Caption,
URL: n.Caption,
Image: n.Image,
}
*dest = news
return true
}

View File

@@ -0,0 +1,28 @@
package json
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
type Stats struct {
CoreVersion string `json:"coreVersion"`
SystemID string `json:"systemId"`
SystemVersion string `json:"systemVersion"`
LastModifiedBy string `json:"lastModifiedBy"`
ModifiedTime int64 `json:"modifiedTime"`
// CompendiumSource string `json:"compendiumSource,omitempty"`
// DuplicateSource string `json:"duplicateSource,omitempty"`
// ExportSource string `json:"exportSource,omitempty"`
}
func (s *Stats) ToDB(dest *db.Stats) bool {
if dest == nil {
return false
}
dest.CoreVersion = s.CoreVersion
dest.SystemID = s.SystemID
dest.SystemVersion = s.SystemVersion
dest.LastModifiedBy = s.LastModifiedBy
dest.ModifiedTime = s.ModifiedTime
return true
}

View File

@@ -0,0 +1,21 @@
package json
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
type Style struct {
Src string `json:"src"`
}
func (s *Style) ToDB(dest **db.Style) bool {
if dest == nil {
return false
}
style := &db.Style{
Src: s.Src,
}
*dest = style
return true
}

View File

@@ -0,0 +1,98 @@
package json
import (
"sync"
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
)
type System struct {
ID string `json:"id"`
Title string `json:"title"`
Description string `json:"description"`
Authors []*Author `json:"authors"`
URL string `json:"url"`
License string `json:"license"`
Bugs string `json:"bugs"`
Changelog string `json:"changelog"`
Media []*Media `json:"media"`
Version string `json:"version"`
Compatibility Compatibility `json:"compatibility"`
Scripts []string `json:"scripts"`
Esmodules []string `json:"esmodules"`
Packs []*Pack `json:"packs"`
Styles []*Style `json:"styles"`
Languages []*Language `json:"languages"`
PackFolders []*Folder `json:"packFolders"`
Relationships Relationships `json:"relationships"`
Socket bool `json:"socket"`
Manifest string `json:"manifest"`
Download string `json:"download"`
Protected bool `json:"protected"`
Exclusive bool `json:"exclusive"`
PersistentStorage bool `json:"persistentStorage"`
DocumentTypes DocumentTypes `json:"documentTypes"`
Background string `json:"background"`
Grid Grid `json:"grid"`
PrimaryTokenAttribute string `json:"primaryTokenAttribute"`
Availability int `json:"availability"`
Locked bool `json:"locked"`
Owned bool `json:"owned"`
Tags []string `json:"tags"`
HasStorage bool `json:"hasStorage"`
// SystemFlags SystemFlags `json:"flags"`
}
func (s *System) ToDB(dest **db.System) bool {
if dest == nil {
return false
}
system := &db.System{
ID: s.ID,
Title: s.Title,
Description: s.Description,
URL: s.URL,
License: s.License,
Bugs: s.Bugs,
Changelog: s.Changelog,
Version: s.Version,
Socket: s.Socket,
Manifest: s.Manifest,
Download: s.Download,
Protected: s.Protected,
Exclusive: s.Exclusive,
PersistentStorage: s.PersistentStorage,
Background: s.Background,
PrimaryTokenAttribute: s.PrimaryTokenAttribute,
Availability: s.Availability,
Locked: s.Locked,
Owned: s.Owned,
HasStorage: s.HasStorage,
}
system.Scripts = make([]string, len(s.Scripts))
copy(system.Scripts, s.Scripts)
system.Esmodules = make([]string, len(s.Esmodules))
copy(system.Esmodules, s.Esmodules)
system.Tags = make([]string, len(s.Tags))
copy(system.Tags, s.Tags)
s.Compatibility.ToDB(&system.Compatibility)
s.Relationships.ToDB(&system.Relationships)
s.DocumentTypes.ToDB(&system.DocumentTypes)
s.Grid.ToDB(&system.Grid)
wg := sync.WaitGroup{}
CopySliceToDBParallel(&wg, &system.Authors, s.Authors)
CopySliceToDBParallel(&wg, &system.Media, s.Media)
CopySliceToDBParallel(&wg, &system.Styles, s.Styles)
CopySliceToDBParallel(&wg, &system.Languages, s.Languages)
CopySliceToDBParallel(&wg, &system.Packs, s.Packs)
CopySliceToDBParallel(&wg, &system.PackFolders, s.PackFolders)
wg.Wait()
*dest = system
return true
}

View File

@@ -0,0 +1,85 @@
package json
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
type Table struct {
Name string `json:"name"`
Results []*TableResult `json:"results"`
Description string `json:"description"`
Formula string `json:"formula"`
ID string `json:"_id"`
Img string `json:"img"`
Replacement bool `json:"replacement"`
DisplayRoll bool `json:"displayRoll"`
Folder string `json:"folder"`
Sort int `json:"sort"`
Ownership map[string]int `json:"ownership,omitempty"`
Stats Stats `json:"_stats"`
// TablesFlags any `json:"flags,omitempty"`
}
func (t *Table) ToDB(dest **db.Table) bool {
if dest == nil {
return false
}
table := &db.Table{
Name: t.Name,
Description: t.Description,
Formula: t.Formula,
ID: t.ID,
Img: t.Img,
Replacement: t.Replacement,
DisplayRoll: t.DisplayRoll,
Folder: t.Folder,
Sort: t.Sort,
}
t.Stats.ToDB(&table.Stats)
OwnershipToDB(&table.Ownership, t.Ownership)
CopySliceToDB(&table.Results, t.Results)
*dest = table
return true
}
type TableResult struct {
Type string `json:"type"`
Weight int `json:"weight"`
Range []int `json:"range"`
Drawn bool `json:"drawn"`
ID string `json:"_id"`
Img string `json:"img"`
Stats Stats `json:"_stats"`
Description string `json:"description"`
Name string `json:"name"`
// ResultsFlags any `json:"flags"`
}
func (t *TableResult) ToDB(dest **db.TableResult) bool {
if dest == nil {
return false
}
tableResult := &db.TableResult{
Type: t.Type,
Weight: t.Weight,
Drawn: t.Drawn,
ID: t.ID,
Img: t.Img,
Description: t.Description,
Name: t.Name,
}
tableResult.Range = make([]int, len(t.Range))
copy(tableResult.Range, t.Range)
t.Stats.ToDB(&tableResult.Stats)
*dest = tableResult
return true
}

View File

@@ -1,132 +0,0 @@
package json
import "time"
type DataTemplate struct {
Id string `json:"id"`
Title string `json:"title"`
Description string `json:"description"`
Authors []DataAuthor `json:"authors,omitempty"`
Url string `json:"url,omitempty"`
Flags Flags `json:"flags,omitempty"`
License string `json:"license,omitempty"`
Readme string `json:"readme,omitempty"`
Bugs string `json:"bugs,omitempty"`
Changelog string `json:"changelog,omitempty"`
Media []DataMedia `json:"media,omitempty"`
Version string `json:"version,omitempty"`
Compatibility Compatibility `json:"compatibility,omitempty"`
Scripts []string `json:"scripts"`
Esmodules []string `json:"esmodules"`
Styles []struct {
Src string `json:"src,omitempty"`
} `json:"styles,omitempty"`
Languages []DataLanguage `json:"languages,omitempty"`
Packs []DataPack `json:"packs,omitempty"`
PackFolder []DataPackFolder `json:"packFolder,omitempty"`
Relationships Relationship `json:"relationships,omitempty"`
Socket bool `json:"socket,omitempty"`
Manifest string `json:"manifest,omitempty"`
Download string `json:"download,omitempty"`
Protected bool `json:"protected,omitempty"`
Exclusive bool `json:"exclusive,omitempty"`
PersistentStorage bool `json:"persistentStorage,omitempty"`
Availability int `json:"availability,omitempty"`
Locked bool `json:"locked,omitempty"`
Owned bool `json:"owned,omitempty"`
HasStorage bool `json:"hasStorage,omitempty"`
//module
CoreTranslation bool `json:"coreTranslation,omitempty"`
Library bool `json:"library,omitempty"`
//system
Background string `json:"background,omitempty"`
Grid DataGrid `json:"grid,omitempty"`
PrimaryTokenAttribute string `json:"primaryTokenAttribute,omitempty"`
//world
System string `json:"system,omitempty"`
JoinTheme string `json:"joinTheme,omitempty"`
CoreVersion string `json:"coreVersion,omitempty"`
SystemVersion string `json:"systemVersion,omitempty"`
LastPlayed string `json:"lastPlayed,omitempty"`
PlayTime int64 `json:"playTime,omitempty"`
NextSession time.Time `json:"nextSession,omitempty"`
}
type DataAuthor struct {
Name string `json:"name,omitempty"`
Url string `json:"url,omitempty"`
Discord string `json:"discord,omitempty"`
flags struct{} `json:"-"`
}
type DataMedia struct {
Type string `json:"type,omitempty"`
Url string `json:"url,omitempty"`
Loop bool `json:"loop,omitempty"`
flags struct{} `json:"-"`
}
type DataLanguage struct {
Lang string `json:"lang,omitempty"`
Name string `json:"name,omitempty"`
Path string `json:"path,omitempty"`
Flags struct{} `json:"-"`
}
type DataPack struct {
Name string `json:"name,omitempty"`
Label string `json:"label,omitempty"`
Banner string `json:"banner,omitempty"`
Path string `json:"path,omitempty"`
Type string `json:"type,omitempty"`
System string `json:"system,omitempty"`
Ownership map[string]string `json:"ownership,omitempty"`
flags struct{} `json:"-"`
}
type DataPackFolder struct {
Name string `json:"name,omitempty"`
Sorting string `json:"sorting,omitempty"`
Color string `json:"color,omitempty"`
Packs []string `json:"packs,omitempty"`
Folders []DataPackFolder `json:"folders,omitempty"`
}
type DataGrid struct {
Type int `json:"type,omitempty"`
Distance int `json:"distance,omitempty"`
Units string `json:"units,omitempty"`
Diagonals int `json:"diagonals,omitempty"`
}
type Compatibility struct {
Minimum string `json:"minimum,omitempty"`
Verified string `json:"verified,omitempty"`
Maximum string `json:"maximum,omitempty"`
}
type Flags struct {
HotReload FlagsHotReload `json:"hotReload"`
Styles []string `json:"styles"`
}
type FlagsHotReload struct {
Enabled bool `json:"enabled"`
Extensions []string `json:"extensions"`
Paths []string `json:"paths"`
}
type Relationship struct {
systems []struct{} `json:"-"`
requires []struct{} `json:"-"`
Recommends []struct {
Id string `json:"id"`
Type string `json:"type"`
compatibility struct{} `json:"-"`
} `json:"recommends"`
conflicts []struct{} `json:"-"`
flags struct{} `json:"-"`
}

View File

@@ -0,0 +1,181 @@
package json
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
type Token struct {
DisplayName int `json:"displayName"`
DisplayBars int `json:"displayBars"`
Disposition int `json:"disposition"`
Sight TokenSight `json:"sight"`
Name string `json:"name"`
ActorLink bool `json:"actorLink"`
AppendNumber bool `json:"appendNumber"`
PrependAdjective bool `json:"prependAdjective"`
Texture TokenTexture `json:"texture"`
Width float64 `json:"width"`
Height float64 `json:"height"`
LockRotation bool `json:"lockRotation"`
Rotation int `json:"rotation"`
Alpha int `json:"alpha"`
Bar1 TokenBar `json:"bar1"`
Bar2 TokenBar `json:"bar2"`
Light Light `json:"light"`
RandomImg bool `json:"randomImg"`
Occludable TokenOccludable `json:"occludable"`
Ring Ring `json:"ring"`
TurnMarker TokenTurnMarker `json:"turnMarker"`
MovementAction any `json:"movementAction"`
// Flags any `json:"flags"`
// DetectionModes []any `json:"detectionModes"`
}
func (t *Token) ToDB(dest **db.Token) bool {
if dest == nil {
return false
}
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(&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
}
type TokenTexture struct {
Src string `json:"src"`
ScaleX float64 `json:"scaleX"`
ScaleY float64 `json:"scaleY"`
OffsetX float64 `json:"offsetX"`
OffsetY float64 `json:"offsetY"`
Rotation float64 `json:"rotation"`
AnchorX float64 `json:"anchorX"`
AnchorY float64 `json:"anchorY"`
Fit string `json:"fit"`
Tint string `json:"tint"`
AlphaThreshold float64 `json:"alphaThreshold"`
}
func (t *TokenTexture) ToDB(dest **db.TokenTexture) bool {
if dest == nil {
return false
}
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
}
type TokenSight struct {
Color string
Enabled bool
Range int
Angle int
VisionMode string
Attenuation float64
Brightness float64
}
func (t *TokenSight) ToDB(dest **db.TokenSight) bool {
if dest == nil {
return false
}
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
}
type TokenBar struct {
Attribute string `json:"attribute"`
}
func (t *TokenBar) ToDB(dest *db.TokenBar) bool {
if dest == nil {
return false
}
dest.Attribute = t.Attribute
return true
}
type TokenOccludable struct {
Radius int `json:"radius"`
}
func (t *TokenOccludable) ToDB(dest *db.TokenOccludable) bool {
if dest == nil {
return false
}
dest.Radius = t.Radius
return true
}
type TokenTurnMarker struct {
Mode int `json:"mode"`
Animation string `json:"animation"`
Src string `json:"src"`
Disposition bool `json:"disposition"`
}
func (t *TokenTurnMarker) ToDB(dest *db.TokenTurnMarker) bool {
if dest == nil {
return false
}
dest.Mode = t.Mode
dest.Animation = t.Animation
dest.Src = t.Src
dest.Disposition = t.Disposition
return true
}

View File

@@ -0,0 +1,45 @@
package json
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
type CoreUpdate struct {
HasUpdate bool `json:"hasUpdate"`
CanUpdate bool `json:"canUpdate"`
CouldReachWebsite bool `json:"couldReachWebsite"`
SlowResponse bool `json:"slowResponse"`
Version string `json:"version"`
Channel string `json:"channel"`
WillDisableModules bool `json:"willDisableModules"`
}
func (c *CoreUpdate) ToDB(dest *db.CoreUpdate) bool {
if dest == nil {
return false
}
dest.HasUpdate = c.HasUpdate
dest.CanUpdate = c.CanUpdate
dest.CouldReachWebsite = c.CouldReachWebsite
dest.SlowResponse = c.SlowResponse
dest.Version = c.Version
dest.Channel = c.Channel
dest.WillDisableModules = c.WillDisableModules
return true
}
type SystemUpdate struct {
HasUpdate bool `json:"hasUpdate"`
Version string `json:"version"`
}
func (s *SystemUpdate) ToDB(dest *db.SystemUpdate) bool {
if dest == nil {
return false
}
dest.HasUpdate = s.HasUpdate
dest.Version = s.Version
return true
}

View File

@@ -0,0 +1,41 @@
package json
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
type User struct {
Name string `json:"name"`
Role int `json:"role"`
ID string `json:"_id"`
Avatar string `json:"avatar"`
Character string `json:"character"`
Color string `json:"color"`
Pronouns string `json:"pronouns"`
Hotbar map[int]string `json:"hotbar,omitempty"`
UsersStats Stats `json:"_stats"`
// Permissions any `json:"permissions"`
// UsersFlags any `json:"flags,omitempty"`
}
func (u *User) ToDB(dest **db.User) bool {
if dest == nil {
return false
}
user := &db.User{
Name: u.Name,
Role: u.Role,
ID: u.ID,
Avatar: u.Avatar,
Character: u.Character,
Color: u.Color,
Pronouns: u.Pronouns,
}
u.UsersStats.ToDB(&user.Stats)
HotbarToDB(&user.Hotbar, u.Hotbar)
*dest = user
return true
}

View File

@@ -1,41 +0,0 @@
package json
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/json/modules"
type User struct {
Name string `json:"name,omitempty"`
Role int `json:"role,omitempty"`
Id string `json:"_id,omitempty"`
avatar struct{} `json:"-"`
Character string `json:"character,omitempty"`
Color string `json:"color,omitempty"`
Pronouns string `json:"pronouns,omitempty"`
Hotbar map[string]string `json:"hotbar,omitempty"`
permissions struct{} `json:"-"`
Flags UserFlags `json:"flags,omitempty"`
Stats UserStats `json:"_stats,omitempty"`
}
type UserFlags struct {
World map[string]bool `json:"world,omitempty"`
Pf2e modules.Pf2eModule `json:"pf2e,omitempty"`
DiceStats modules.DiceStats `json:"diceStats,omitempty"`
}
type UserStats struct {
compendiumSource struct{} `json:"-"`
duplicateSource struct{} `json:"-"`
exportSource struct{} `json:"-"`
CoreVersion string `json:"coreVersion,omitempty"`
SystemId string `json:"systemId,omitempty"`
SystemVersion string `json:"systemVersion,omitempty"`
CreatedTime int64 `json:"createdTime,omitempty"`
ModifiedTime int64 `json:"modifiedTime,omitempty"`
LastModifiedBy string `json:"lastModifiedBy,omitempty"`
}
type Users []User
func (users Users) GetById(id int) *User {
return &users[id]
}

View File

@@ -0,0 +1,68 @@
package json
import (
"errors"
"sync"
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
)
var (
ErrorSetupMoreThanOne = errors.New("More than one setup data")
)
func OwnershipToDB(dest *[]db.OwnershipString, src map[string]int) {
*dest = make([]db.OwnershipString, len(src))
i := 0
for k, v := range src {
(*dest)[i].Key = k
(*dest)[i].Value = v
i++
}
}
func HotbarToDB(dest *[]db.UserHotbar, src map[int]string) {
*dest = make([]db.UserHotbar, len(src))
i := 0
for k, v := range src {
(*dest)[i].Key = k
(*dest)[i].Value = v
i++
}
}
func PackageWarningsToDB(dest *[]*db.PackageWarning, src map[string]PackageWarningsData) {
*dest = make([]*db.PackageWarning, len(src))
i := 0
for k, v := range src {
(*dest)[i] = &db.PackageWarning{Key: k}
v.ToDB(&(*dest)[i].Value)
i++
}
}
type CastableToDB[destType any] interface {
ToDB(*destType) bool
}
func CopySliceToDB[destType any, srcType CastableToDB[destType]](dest *[]destType, src []srcType) bool {
if dest == nil {
return false
}
*dest = make([]destType, len(src))
for i := range src {
src[i].ToDB(&(*dest)[i])
}
return true
}
func CopySliceToDBParallel[destType any, srcType CastableToDB[destType]](wg *sync.WaitGroup, dest *[]destType, src []srcType) {
wg.Go(func() {
CopySliceToDB(dest, src)
})
}

View File

@@ -0,0 +1,59 @@
package json
import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
type Wall struct {
C []int `json:"c"`
Light int `json:"light"`
Move int `json:"move"`
Sight int `json:"sight"`
Sound int `json:"sound"`
Dir int `json:"dir"`
Door int `json:"door"`
Ds int `json:"ds"`
ID string `json:"_id"`
Threshold Threshold `json:"threshold"`
Animation any `json:"animation"`
// WallsFlags any `json:"flags"`
}
func (w *Wall) ToDB(dest *db.Wall) bool {
if dest == nil {
return false
}
dest.Light = w.Light
dest.Move = w.Move
dest.Sight = w.Sight
dest.Sound = w.Sound
dest.Dir = w.Dir
dest.Door = w.Door
dest.Ds = w.Ds
dest.ID = w.ID
w.Threshold.ToDB(&dest.Threshold)
dest.C = make([]int, len(w.C))
copy(dest.C, w.C)
return true
}
type Threshold struct {
Light int `json:"light"`
Sight int `json:"sight"`
Sound int `json:"sound"`
Attenuation bool `json:"attenuation"`
}
func (t *Threshold) ToDB(dest *db.Threshold) bool {
if dest == nil {
return false
}
dest.Light = t.Light
dest.Sight = t.Sight
dest.Sound = t.Sound
dest.Attenuation = t.Attenuation
return true
}

Some files were not shown because too many files have changed in this diff Show More