Compare commits

...

2 Commits

46 changed files with 419 additions and 179 deletions

View File

@@ -1,7 +1,7 @@
-include .env -include .env
api/run: api/run:
go run ./cmd/api -foundry_pass=${FOUNDRY_PASS} -foundry_host=${FOUNDRY_HOST} -port=${LISTEN_PORT} -env=development -log_level=${LOG_LEVEL} -db-dsn=${DB_DSN} -foundry-worlds=${FOUNDRY_WORLDS} go run ./cmd/api -foundry_pass=${FOUNDRY_PASS} -foundry_host=${FOUNDRY_HOST} -port=${LISTEN_PORT} -env=development -log_level=${LOG_LEVEL} -db-dsn=${DB_DSN} -foundry-worlds=${FOUNDRY_WORLDS} -world-user=${WORLD_USER} -world-pass=${WORLD_PASS}
db/migration/new: db/migration/new:
@echo 'Creating migration files for ${name}...' @echo 'Creating migration files for ${name}...'

View File

@@ -16,7 +16,7 @@ func (app *application) ShowText(w http.ResponseWriter, r *http.Request) {
return return
} }
w.Write([]byte(fmt.Sprintf("Got world with name %s, core_version - %s, next_session - %v", world.ID, world.CoreVersion, world.NextSession))) fmt.Fprintf(w, "Got world with name %s, core_version - %s, next_session - %v", world.ID, world.CoreVersion, world.NextSession)
} }
func (app *application) WhenNextSession(w http.ResponseWriter, r *http.Request) { func (app *application) WhenNextSession(w http.ResponseWriter, r *http.Request) {

View File

@@ -7,12 +7,12 @@ import (
"log/slog" "log/slog"
"net/http/cookiejar" "net/http/cookiejar"
"os" "os"
"strings"
"time" "time"
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry" "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry"
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/requests" "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/requests"
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/transport" "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/transport"
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/types"
"github.com/jmoiron/sqlx" "github.com/jmoiron/sqlx"
_ "github.com/mattn/go-sqlite3" _ "github.com/mattn/go-sqlite3"
@@ -95,10 +95,19 @@ func (app *application) parseFlags() *transport.FoundryTransportData {
flag.StringVar(&logLevelStr, "log_level", "info", "Password to connect to Foundry(debug|info|warn|error)") flag.StringVar(&logLevelStr, "log_level", "info", "Password to connect to Foundry(debug|info|warn|error)")
var worlds string var worlds string
flag.StringVar(&worlds, "foundry-worlds", "", "Worlds that initialize in db on startup") flag.StringVar(&worlds, "foundry-worlds", "", "Worlds that initialize in db on startup (format: \"test\", \"test1,test2,test3\", \"test1,test2,test3\")")
var users string
flag.StringVar(&users, "world-user", "", "Usernames to authenticate the world (format: \"test\", \"test1,test2,test3\", \"testForAll\")")
var passwords string
flag.StringVar(&passwords, "world-pass", "", "Passwords to authenticate the world (format: \"test\", \"test1,test2,test3\", \"testForAll\")")
flag.Parse() flag.Parse()
foundryTransportData.Worlds = strings.Split(worlds, ",") worldsData, err := types.CreateWorldDataSlice(worlds, users, passwords)
if err != nil {
app.slogger.Warn("Got err on parsing authentication world data", "err", err)
return nil
}
foundryTransportData.Worlds = worldsData
app.cfg.mode = service_mode(mode) app.cfg.mode = service_mode(mode)
@@ -120,6 +129,10 @@ func main() {
app := application{foundryApp: &foundry.FoundryApi{}} app := application{foundryApp: &foundry.FoundryApi{}}
foundryTransportData := app.parseFlags() foundryTransportData := app.parseFlags()
if foundryTransportData == nil {
return
}
jar, err := cookiejar.New(nil) jar, err := cookiejar.New(nil)
if err != nil { if err != nil {
app.slogger.Error("Error", "text", err.Error()) app.slogger.Error("Error", "text", err.Error())

View File

@@ -268,7 +268,7 @@ CREATE TABLE IF NOT EXISTS ownership_string (
key_ VARCHAR(128) NOT NULL, key_ VARCHAR(128) NOT NULL,
value INTEGER NOT NULL, value INTEGER NOT NULL,
card_deck_id TEXT UNIQUE, card_deck_id TEXT,
FOREIGN KEY (card_deck_id) REFERENCES card_deck(id) ON DELETE CASCADE FOREIGN KEY (card_deck_id) REFERENCES card_deck(id) ON DELETE CASCADE
); );

View File

@@ -22,8 +22,7 @@ func (msg WsSessionMsg) Action(tr *transport.FoundryTransport, status *types.Fou
} }
tr.IsDbInit = true tr.IsDbInit = true
go tr.FillDBWithFoundryData() return tr.FillDBWithFoundryData()
return nil
} }
func (msg WsSessionMsg) OnActiveWorld(tr *transport.FoundryTransport) error { func (msg WsSessionMsg) OnActiveWorld(tr *transport.FoundryTransport) error {
@@ -32,7 +31,12 @@ func (msg WsSessionMsg) OnActiveWorld(tr *transport.FoundryTransport) error {
return msg.HandleLoggedInUser(tr) return msg.HandleLoggedInUser(tr)
} }
err := tr.LogInToWorld() userId, userPass, err := tr.GetUserIdAndPass()
if err != nil {
return err
}
err = tr.LogInToWorld(userId, userPass)
if err != nil { if err != nil {
return err return err
} }
@@ -45,8 +49,7 @@ func (msg WsSessionMsg) OnActiveWorld(tr *transport.FoundryTransport) error {
func (msg WsSessionMsg) HandleLoggedInUser(tr *transport.FoundryTransport) error { func (msg WsSessionMsg) HandleLoggedInUser(tr *transport.FoundryTransport) error {
if tr.LoggedInChan == nil { if tr.LoggedInChan == nil {
tr.Logger.Info("World had been started before application was started. Run insertion of world data") tr.Logger.Info("World had been started before application was started. Run insertion of world data")
go tr.InsertGameToDB() return tr.InsertGameToDB()
return nil
} }
select { select {

View File

@@ -25,6 +25,5 @@ func (msg WsShutdownMsg) Action(tr *transport.FoundryTransport, status *types.Fo
} }
tr.IsDbInit = true tr.IsDbInit = true
go tr.FillDBWithFoundryData() return tr.FillDBWithFoundryData()
return nil
} }

View File

@@ -90,15 +90,24 @@ func (foundry *FoundryApi) ServeWebSocket() error {
continue continue
} }
err = data.Action(foundry.Transport, &foundry.Status) go func() {
if err != nil { errAction := data.Action(foundry.Transport, &foundry.Status)
wsChannels.Err() <- &types.FoundryError{Direction: types.ReaderCode, Err: err, Type: types.WebSocketCode}
continue if errAction != nil {
} wsChannels.Err() <- &types.FoundryError{Direction: types.ReaderCode, Err: errAction, Type: types.WebSocketCode}
}
}()
case types.RespDataCode: case types.RespDataCode:
foundry.Transport.ExchangeChan.Msgs[message.Id] = make(chan []byte, 1) tr := foundry.Transport
foundry.Transport.ExchangeChan.Msgs[message.Id] <- []byte(message.MsgJson) func() {
go foundry.Transport.CloseMsgChannel(message.Id, 5*time.Second) tr.ChanMutex.Lock()
defer tr.ChanMutex.Unlock()
tr.ExchangeChan.Msgs[message.Id] = make(chan []byte, 1)
tr.ExchangeChan.Msgs[message.Id] <- []byte(message.MsgJson)
go tr.CloseMsgChannel(tr.ExchangeChan.Msgs[message.Id], message.Id, 5*time.Second)
}()
default: default:
} }
case err = <-wsChannels.Err(): case err = <-wsChannels.Err():

View File

@@ -34,11 +34,11 @@ func (a *Actor) Query(data *InsertId[uint]) {
} }
func (a *Actor) InsertObjects(tx *sqlx.Tx) error { func (a *Actor) InsertObjects(tx *sqlx.Tx) error {
group, ctx := errgroup.WithContext(context.Background()) group, _ := errgroup.WithContext(context.Background())
relId := InsertId[string]{id: a.ID, fieldName: "actor_id"} relId := InsertId[string]{id: a.ID, fieldName: "actor_id"}
InsertWithCtxParallel(group, ctx, tx, a.PrototypeToken, relId) InsertWithCtxParallel(group, tx, a.PrototypeToken, relId)
InsertWithCtxParallel(group, ctx, tx, a.Stats, relId) InsertWithCtxParallel(group, tx, a.Stats, relId)
InsertSliceParallel(group, tx, a.Ownership, relId) InsertSliceParallel(group, tx, a.Ownership, relId)
InsertSliceParallel(group, tx, a.Items, relId) InsertSliceParallel(group, tx, a.Items, relId)
@@ -75,6 +75,10 @@ func (a *Actor) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[uint]
args := []any{data.id, a.ID, a.Img, a.Name, a.Type, a.Folder, a.Sort} args := []any{data.id, a.ID, a.Img, a.Name, a.Type, a.Folder, a.Sort}
mutex := GetMutex("actor_insert")
mutex.Lock()
defer mutex.Unlock()
var isInserted bool var isInserted bool
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&isInserted) err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&isInserted)
if err != nil { if err != nil {

View File

@@ -46,6 +46,10 @@ func (a *Author) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[stri
args := []any{data.id, a.Name, a.URL, a.Email, a.Discord} args := []any{data.id, a.Name, a.URL, a.Email, a.Discord}
mutex := GetMutex("author_insert")
mutex.Lock()
defer mutex.Unlock()
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&a.ID) err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&a.ID)
if err != nil { if err != nil {
return err return err

View File

@@ -38,10 +38,10 @@ func (c *CardDeck) Query(data *InsertId[uint]) {
} }
func (c *CardDeck) InsertObjects(tx *sqlx.Tx) error { func (c *CardDeck) InsertObjects(tx *sqlx.Tx) error {
group, ctx := errgroup.WithContext(context.Background()) group, _ := errgroup.WithContext(context.Background())
relId := InsertId[string]{id: c.ID, fieldName: "card_deck_id"} relId := InsertId[string]{id: c.ID, fieldName: "card_deck_id"}
InsertWithCtxParallel(group, ctx, tx, c.Stats, relId) InsertWithCtxParallel(group, tx, c.Stats, relId)
InsertSliceParallel(group, tx, c.Ownership, relId) InsertSliceParallel(group, tx, c.Ownership, relId)
InsertSliceParallel(group, tx, c.Cards, relId) InsertSliceParallel(group, tx, c.Cards, relId)
@@ -121,11 +121,11 @@ func (c *Card) Query(data *InsertId[string]) {
} }
func (c *Card) InsertObjects(tx *sqlx.Tx) error { func (c *Card) InsertObjects(tx *sqlx.Tx) error {
group, ctx := errgroup.WithContext(context.Background()) group, _ := errgroup.WithContext(context.Background())
relId := InsertId[string]{id: c.ID, fieldName: "card_id"} relId := InsertId[string]{id: c.ID, fieldName: "card_id"}
InsertWithCtxParallel(group, ctx, tx, c.Back, relId) InsertWithCtxParallel(group, tx, c.Back, relId)
InsertWithCtxParallel(group, ctx, tx, c.Stats, relId) InsertWithCtxParallel(group, tx, c.Stats, relId)
InsertSliceParallel(group, tx, c.Faces, relId) InsertSliceParallel(group, tx, c.Faces, relId)
err := group.Wait() err := group.Wait()

View File

@@ -34,10 +34,10 @@ func (c *Combat) Query(data *InsertId[uint]) {
} }
func (c *Combat) InsertObjects(tx *sqlx.Tx) error { func (c *Combat) InsertObjects(tx *sqlx.Tx) error {
group, ctx := errgroup.WithContext(context.Background()) group, _ := errgroup.WithContext(context.Background())
relId := InsertId[string]{id: c.ID, fieldName: "combat_id", tableName: "combat_groups"} relId := InsertId[string]{id: c.ID, fieldName: "combat_id", tableName: "combat_groups"}
InsertWithCtxParallel(group, ctx, tx, c.Stats, relId) InsertWithCtxParallel(group, tx, c.Stats, relId)
InsertSimpleSliceParallel(group, tx, c.Groups, &relId) InsertSimpleSliceParallel(group, tx, c.Groups, &relId)
InsertSliceParallel(group, tx, c.Combatants, relId) InsertSliceParallel(group, tx, c.Combatants, relId)
@@ -113,9 +113,9 @@ func (c *Combatant) Query(data *InsertId[string]) {
func (c *Combatant) InsertObjects(tx *sqlx.Tx) error { func (c *Combatant) InsertObjects(tx *sqlx.Tx) error {
relId := InsertId[string]{id: c.ID, fieldName: "combatant_id"} relId := InsertId[string]{id: c.ID, fieldName: "combatant_id"}
group, ctx := errgroup.WithContext(context.Background()) group, _ := errgroup.WithContext(context.Background())
InsertWithCtxParallel(group, ctx, tx, c.Stats, relId) InsertWithCtxParallel(group, tx, c.Stats, relId)
err := group.Wait() err := group.Wait()
if err != nil && !errors.Is(err, sql.ErrNoRows) { if err != nil && !errors.Is(err, sql.ErrNoRows) {

View File

@@ -44,6 +44,10 @@ func (c Compatibility) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertI
args := []any{data.id, c.Minimum, c.Verified, c.Maximum} args := []any{data.id, c.Minimum, c.Verified, c.Maximum}
mutex := GetMutex("compatibility_insert")
mutex.Lock()
defer mutex.Unlock()
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&c.ID) err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&c.ID)
if err != nil { if err != nil {
return err return err

View File

@@ -52,6 +52,10 @@ func (d DocumentTypes) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertI
args := []any{data.id} args := []any{data.id}
mutex := GetMutex("document_types_insert")
mutex.Lock()
defer mutex.Unlock()
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&d.ID) err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&d.ID)
if err != nil { if err != nil {
return err return err

View File

@@ -100,10 +100,10 @@ func (w *WorldFolder) Query(data *InsertId[uint]) {
} }
func (w *WorldFolder) InsertObjects(tx *sqlx.Tx) error { func (w *WorldFolder) InsertObjects(tx *sqlx.Tx) error {
group, ctx := errgroup.WithContext(context.Background()) group, _ := errgroup.WithContext(context.Background())
relId := InsertId[string]{id: w.ID, fieldName: "world_folder_id"} relId := InsertId[string]{id: w.ID, fieldName: "world_folder_id"}
InsertWithCtxParallel(group, ctx, tx, w.Stats, relId) InsertWithCtxParallel(group, tx, w.Stats, relId)
err := group.Wait() err := group.Wait()
if err != nil && !errors.Is(err, sql.ErrNoRows) { if err != nil && !errors.Is(err, sql.ErrNoRows) {

View File

@@ -4,6 +4,7 @@ import (
"context" "context"
"database/sql" "database/sql"
"errors" "errors"
"fmt"
"strconv" "strconv"
"time" "time"
@@ -47,53 +48,81 @@ type Game struct {
// Scenes []Scene // Scenes []Scene
} }
func (g *Game) InsertObjects(tx *sqlx.Tx) error { func (g *Game) InsertObjects(db *sqlx.DB) error {
relData := InsertId[uint]{id: g.ID, fieldName: "game_id"} relData := InsertId[uint]{id: g.ID, fieldName: "game_id"}
group, ctx := errgroup.WithContext(context.Background())
InsertWithCtxParallel(group, ctx, tx, &g.Addresses, relData) err := func() error {
InsertWithCtxParallel(group, ctx, tx, &g.Files, relData) tx := db.MustBegin()
InsertWithCtxParallel(group, ctx, tx, &g.Options, relData) defer tx.Rollback()
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"} groupFunc, _ := errgroup.WithContext(context.Background())
InsertWithCtxParallel(group, ctx, tx, g.World, relDataString)
InsertWithCtxParallel(group, ctx, tx, g.System, relDataString)
InsertSimpleSliceParallel(group, tx, g.ActiveUsers, InsertWithCtxParallel(groupFunc, tx, &g.Addresses, relData)
&InsertId[uint]{id: g.ID, fieldName: "game_id", tableName: "active_users"}) InsertWithCtxParallel(groupFunc, tx, &g.Files, relData)
InsertWithCtxParallel(groupFunc, tx, &g.Options, relData)
InsertWithCtxParallel(groupFunc, tx, &g.Release, relData)
InsertWithCtxParallel(groupFunc, tx, &g.CoreUpdate, relData)
InsertWithCtxParallel(groupFunc, tx, &g.SystemUpdate, relData)
InsertSliceParallel(group, tx, g.Modules, relData) relDataString := InsertId[string]{id: strconv.FormatUint(uint64(g.ID), 10), fieldName: "game_id"}
InsertSliceParallel(group, tx, g.PackageWarnings, relData) InsertWithCtxParallel(groupFunc, tx, g.World, relDataString)
InsertSliceParallel(group, tx, g.Packs, relDataString) InsertWithCtxParallel(groupFunc, tx, g.System, 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() InsertSimpleSliceParallel(groupFunc, tx, g.ActiveUsers,
if err != nil && !errors.Is(err, sql.ErrNoRows) { &InsertId[uint]{id: g.ID, fieldName: "game_id", tableName: "active_users"})
return err
}
InsertSliceParallelTimeout(group, tx, g.Items, InsertSliceParallel(groupFunc, tx, g.Modules, relData)
InsertId[string]{id: strconv.FormatUint(uint64(g.ID), 10), fieldName: "game_id"}, InsertSliceParallel(groupFunc, tx, g.PackageWarnings, relData)
15*time.Second) InsertSliceParallel(groupFunc, tx, g.Packs, relDataString)
InsertSliceParallelTimeout(group, tx, g.Actors, relData, 15*time.Second) InsertSliceParallel(groupFunc, tx, g.Messages, relData)
InsertSliceParallel(groupFunc, tx, g.Combats, relData)
InsertSliceParallel(groupFunc, tx, g.CardDeck, relData)
InsertSliceParallel(groupFunc, tx, g.Users, relData)
InsertSliceParallel(groupFunc, tx, g.Macros, relData)
InsertSliceParallel(groupFunc, tx, g.Folders, relData)
err = group.Wait() errFunc := groupFunc.Wait()
if err != nil && !errors.Is(err, sql.ErrNoRows) { if errFunc != nil && !errors.Is(errFunc, sql.ErrNoRows) {
return err return errFunc
} }
return nil return tx.Commit()
}()
err = func() error {
tx := db.MustBegin()
defer tx.Rollback()
groupFunc, _ := errgroup.WithContext(context.Background())
InsertSliceParallel(groupFunc, tx, g.Settings, relData)
InsertSliceParallel(groupFunc, tx, g.Journals, relData)
InsertSliceParallel(groupFunc, tx, g.Tables, relData)
InsertSliceParallel(groupFunc, tx, g.Playlists, relData)
errFunc := groupFunc.Wait()
if errFunc != nil && !errors.Is(errFunc, sql.ErrNoRows) {
return errFunc
}
return tx.Commit()
}()
err = func() error {
tx := db.MustBegin()
defer tx.Rollback()
groupFunc, _ := errgroup.WithContext(context.Background())
InsertSliceParallelTimeout(groupFunc, tx, g.Items,
InsertId[string]{id: strconv.FormatUint(uint64(g.ID), 10), fieldName: "game_id"},
15*time.Second)
InsertSliceParallelTimeout(groupFunc, tx, g.Actors, relData, 15*time.Second)
errFunc := groupFunc.Wait()
if errFunc != nil && !errors.Is(errFunc, sql.ErrNoRows) {
return errFunc
}
return tx.Commit()
}()
return err
} }
func (g *Game) Insert(db *sqlx.DB) error { func (g *Game) Insert(db *sqlx.DB) error {
@@ -114,11 +143,15 @@ func (g *Game) Insert(db *sqlx.DB) error {
if err != nil { if err != nil {
return err return err
} }
err = tx.Commit()
err = g.InsertObjects(tx)
if err != nil { if err != nil {
return err return err
} }
return tx.Commit() err = g.InsertObjects(db)
if err != nil {
fmt.Printf("%v\n", err)
}
return err
} }

View File

@@ -76,6 +76,10 @@ func (i *Index) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[strin
args := []any{i.ID, i.Folder, i.Img, i.Name, i.Type} args := []any{i.ID, i.Folder, i.Img, i.Name, i.Type}
mutex := GetMutex("index_insert")
mutex.Lock()
defer mutex.Unlock()
_, err := tx.ExecContext(ctx, data.query, args...) _, err := tx.ExecContext(ctx, data.query, args...)
if err != nil { if err != nil {
return err return err

View File

@@ -33,10 +33,10 @@ func (i *Item) Query(data *InsertId[string]) {
} }
func (i *Item) InsertObjects(tx *sqlx.Tx) error { func (i *Item) InsertObjects(tx *sqlx.Tx) error {
group, ctx := errgroup.WithContext(context.Background()) group, _ := errgroup.WithContext(context.Background())
relId := InsertId[string]{id: i.ID, fieldName: "item_id"} relId := InsertId[string]{id: i.ID, fieldName: "item_id"}
InsertWithCtxParallel(group, ctx, tx, i.Stats, relId) InsertWithCtxParallel(group, tx, i.Stats, relId)
InsertSliceParallel(group, tx, i.Ownership, relId) InsertSliceParallel(group, tx, i.Ownership, relId)
err := group.Wait() err := group.Wait()
@@ -72,6 +72,10 @@ func (i *Item) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string
args := []any{data.id, i.ID, i.Img, i.Name, i.Type, i.Folder, i.Sort} args := []any{data.id, i.ID, i.Img, i.Name, i.Type, i.Folder, i.Sort}
mutex := GetMutex("item_insert")
mutex.Lock()
defer mutex.Unlock()
var isInserted bool var isInserted bool
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&isInserted) err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&isInserted)
if err != nil { if err != nil {

View File

@@ -35,13 +35,13 @@ func (j *JournalPage) Query(data *InsertId[string]) {
} }
func (j *JournalPage) InsertObjects(tx *sqlx.Tx) error { func (j *JournalPage) InsertObjects(tx *sqlx.Tx) error {
group, ctx := errgroup.WithContext(context.Background()) group, _ := errgroup.WithContext(context.Background())
relId := InsertId[string]{id: j.ID, fieldName: "journal_page_id"} relId := InsertId[string]{id: j.ID, fieldName: "journal_page_id"}
InsertWithCtxParallel(group, ctx, tx, j.Text, relId) InsertWithCtxParallel(group, tx, j.Text, relId)
InsertWithCtxParallel(group, ctx, tx, j.Title, relId) InsertWithCtxParallel(group, tx, j.Title, relId)
InsertWithCtxParallel(group, ctx, tx, j.Video, relId) InsertWithCtxParallel(group, tx, j.Video, relId)
InsertWithCtxParallel(group, ctx, tx, j.Stats, relId) InsertWithCtxParallel(group, tx, j.Stats, relId)
InsertSliceParallel(group, tx, j.Ownership, relId) InsertSliceParallel(group, tx, j.Ownership, relId)

View File

@@ -145,6 +145,10 @@ func (l *Language) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[st
args := []any{data.id, l.Lang, l.Name, l.Path} args := []any{data.id, l.Lang, l.Name, l.Path}
mutex := GetMutex("language_insert")
mutex.Lock()
defer mutex.Unlock()
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&l.ID) err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&l.ID)
if err != nil { if err != nil {
return err return err

View File

@@ -39,11 +39,11 @@ func (l *Light) Query(data *InsertId[uint]) {
} }
func (l *Light) InsertObjects(tx *sqlx.Tx) error { func (l *Light) InsertObjects(tx *sqlx.Tx) error {
group, ctx := errgroup.WithContext(context.Background()) group, _ := errgroup.WithContext(context.Background())
relId := InsertId[uint]{id: l.ID, fieldName: "token_light_id"} relId := InsertId[uint]{id: l.ID, fieldName: "token_light_id"}
InsertWithCtxParallel(group, ctx, tx, l.LightAnimation, relId) InsertWithCtxParallel(group, tx, l.LightAnimation, relId)
InsertWithCtxParallel(group, ctx, tx, l.LightDarkness, relId) InsertWithCtxParallel(group, tx, l.LightDarkness, relId)
err := group.Wait() err := group.Wait()
if err != nil && !errors.Is(err, sql.ErrNoRows) { if err != nil && !errors.Is(err, sql.ErrNoRows) {

View File

@@ -35,10 +35,10 @@ func (m *Macro) Query(data *InsertId[uint]) {
} }
func (m *Macro) InsertObjects(tx *sqlx.Tx) error { func (m *Macro) InsertObjects(tx *sqlx.Tx) error {
group, ctx := errgroup.WithContext(context.Background()) group, _ := errgroup.WithContext(context.Background())
relId := InsertId[string]{id: m.ID, fieldName: "macro_id"} relId := InsertId[string]{id: m.ID, fieldName: "macro_id"}
InsertWithCtxParallel(group, ctx, tx, m.Stats, relId) InsertWithCtxParallel(group, tx, m.Stats, relId)
InsertSliceParallel(group, tx, m.Ownership, relId) InsertSliceParallel(group, tx, m.Ownership, relId)
err := group.Wait() err := group.Wait()

View File

@@ -44,6 +44,10 @@ func (m *Media) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[strin
args := []any{data.id, m.Type, m.URL, m.Caption} args := []any{data.id, m.Type, m.URL, m.Caption}
mutex := GetMutex("media_insert")
mutex.Lock()
defer mutex.Unlock()
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&m.ID) err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&m.ID)
if err != nil { if err != nil {
return err return err

View File

@@ -40,10 +40,10 @@ func (m *Message) Query(data *InsertId[uint]) {
func (m *Message) InsertObjects(tx *sqlx.Tx) error { func (m *Message) InsertObjects(tx *sqlx.Tx) error {
relId := InsertId[string]{id: m.ID, fieldName: "message_id"} relId := InsertId[string]{id: m.ID, fieldName: "message_id"}
group, ctx := errgroup.WithContext(context.Background()) group, _ := errgroup.WithContext(context.Background())
InsertWithCtxParallel(group, ctx, tx, m.Stats, relId) InsertWithCtxParallel(group, tx, m.Stats, relId)
InsertWithCtxParallel(group, ctx, tx, m.Speaker, relId) InsertWithCtxParallel(group, tx, m.Speaker, relId)
InsertSimpleSliceParallel(group, tx, m.Whisper, &InsertId[string]{id: m.ID, fieldName: "message_id", tableName: "message_whisper"}) 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"}) InsertSimpleSliceParallel(group, tx, m.Rolls, &InsertId[string]{id: m.ID, fieldName: "message_id", tableName: "message_rolls"})

View File

@@ -66,11 +66,11 @@ func (m *Module) ConnectGameQuery(data *InsertId[uint]) {
func (m *Module) InsertObjects(tx *sqlx.Tx) error { func (m *Module) InsertObjects(tx *sqlx.Tx) error {
relId := InsertId[string]{id: m.ID, fieldName: "module_id"} relId := InsertId[string]{id: m.ID, fieldName: "module_id"}
group, ctx := errgroup.WithContext(context.Background()) group, _ := errgroup.WithContext(context.Background())
InsertWithCtxParallel(group, ctx, tx, m.DocumentTypes, relId) InsertWithCtxParallel(group, tx, m.DocumentTypes, relId)
InsertWithCtxParallel(group, ctx, tx, m.Relationships, relId) InsertWithCtxParallel(group, tx, m.Relationships, relId)
InsertWithCtxParallel(group, ctx, tx, m.Compatibility, relId) InsertWithCtxParallel(group, tx, m.Compatibility, relId)
scriptRelId := &InsertId[string]{id: m.ID, fieldName: "module_id", tableName: "scripts"} scriptRelId := &InsertId[string]{id: m.ID, fieldName: "module_id", tableName: "scripts"}
InsertSimpleSliceParallel(group, tx, m.Scripts, scriptRelId) InsertSimpleSliceParallel(group, tx, m.Scripts, scriptRelId)

View File

@@ -88,6 +88,10 @@ func (o OwnershipString) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *Inser
args := []any{data.id, o.Key, o.Value} args := []any{data.id, o.Key, o.Value}
mutex := GetMutex("ownership_string_insert")
mutex.Lock()
defer mutex.Unlock()
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&o.ID) err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&o.ID)
if err != nil { if err != nil {
return err return err

View File

@@ -48,9 +48,9 @@ func (p *Pack) ConnectGameQuery(data *InsertId[string]) {
} }
func (p *Pack) InsertObjects(tx *sqlx.Tx) error { func (p *Pack) InsertObjects(tx *sqlx.Tx) error {
group, ctx := errgroup.WithContext(context.Background()) group, _ := errgroup.WithContext(context.Background())
InsertWithCtxParallel(group, ctx, tx, p.Ownership, InsertWithCtxParallel(group, tx, p.Ownership,
InsertId[string]{id: p.ID, fieldName: "pack_id"}) InsertId[string]{id: p.ID, fieldName: "pack_id"})
relId := InsertId[string]{id: p.ID, fieldName: "pack_id"} relId := InsertId[string]{id: p.ID, fieldName: "pack_id"}
@@ -88,6 +88,10 @@ func (p *Pack) Insert(tx *sqlx.Tx, data *InsertId[string]) error {
args := []any{dataId, p.ID, p.Name, p.Label, p.Banner, p.Path, p.Type, args := []any{dataId, p.ID, p.Name, p.Label, p.Banner, p.Path, p.Type,
p.System, p.PackageType, p.PackageName} p.System, p.PackageType, p.PackageName}
mutex := GetMutex("pack_insert")
mutex.Lock()
defer mutex.Unlock()
res, err := tx.Exec(data.query, args...) res, err := tx.Exec(data.query, args...)
if err != nil { if err != nil {
return err return err
@@ -134,6 +138,10 @@ func (p *Pack) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string
args := []any{dataId, p.ID, p.Name, p.Label, p.Banner, p.Path, p.Type, args := []any{dataId, p.ID, p.Name, p.Label, p.Banner, p.Path, p.Type,
p.System, p.PackageType, p.PackageName} p.System, p.PackageType, p.PackageName}
mutex := GetMutex("pack_insert")
mutex.Lock()
defer mutex.Unlock()
res, err := tx.ExecContext(ctx, data.query, args...) res, err := tx.ExecContext(ctx, data.query, args...)
if err != nil { if err != nil {
return err return err

View File

@@ -48,6 +48,10 @@ func (p *PackageWarning) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *Inser
args := []any{data.id, p.Key} args := []any{data.id, p.Key}
mutex := GetMutex("package_warning_insert")
mutex.Lock()
defer mutex.Unlock()
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&p.ID) err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&p.ID)
if err != nil { if err != nil {
return err return err

View File

@@ -38,10 +38,10 @@ func (p *Playlist) Query(data *InsertId[uint]) {
} }
func (p *Playlist) InsertObjects(tx *sqlx.Tx) error { func (p *Playlist) InsertObjects(tx *sqlx.Tx) error {
group, ctx := errgroup.WithContext(context.Background()) group, _ := errgroup.WithContext(context.Background())
relId := InsertId[string]{id: p.ID, fieldName: "playlist_id"} relId := InsertId[string]{id: p.ID, fieldName: "playlist_id"}
InsertWithCtxParallel(group, ctx, tx, p.Stats, relId) InsertWithCtxParallel(group, tx, p.Stats, relId)
InsertSliceParallel(group, tx, p.Ownership, relId) InsertSliceParallel(group, tx, p.Ownership, relId)
InsertSliceParallel(group, tx, p.Sounds, relId) InsertSliceParallel(group, tx, p.Sounds, relId)

View File

@@ -60,6 +60,10 @@ func (r Relationships) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertI
args := []any{data.id} args := []any{data.id}
mutex := GetMutex("relationships_insert")
mutex.Lock()
defer mutex.Unlock()
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&r.ID) err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&r.ID)
if err != nil { if err != nil {
return err return err
@@ -106,8 +110,8 @@ func (r RelationshipsData) Insert(tx *sqlx.Tx, data *InsertId[uint]) error {
return err return err
} }
group, ctx := errgroup.WithContext(context.Background()) group, _ := 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)}) InsertWithCtxParallel(group, tx, r.Compatibility, InsertId[string]{id: strconv.FormatUint(uint64(r.ID), 10), fieldName: fmt.Sprintf("%s_id", data.tableName)})
return group.Wait() return group.Wait()
} }
@@ -124,8 +128,8 @@ func (r RelationshipsData) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *Ins
return err return err
} }
group, ctx := errgroup.WithContext(context.Background()) group, _ := 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)}) InsertWithCtxParallel(group, tx, r.Compatibility, InsertId[string]{id: strconv.FormatUint(uint64(r.ID), 10), fieldName: fmt.Sprintf("%s_id", data.tableName)})
return group.Wait() return group.Wait()
} }

View File

@@ -27,11 +27,11 @@ func (r *Ring) Query(data *InsertId[uint]) {
} }
func (r *Ring) InsertObjects(tx *sqlx.Tx) error { func (r *Ring) InsertObjects(tx *sqlx.Tx) error {
group, ctx := errgroup.WithContext(context.Background()) group, _ := errgroup.WithContext(context.Background())
relId := InsertId[uint]{id: r.ID, fieldName: "ring_id"} relId := InsertId[uint]{id: r.ID, fieldName: "ring_id"}
InsertWithCtxParallel(group, ctx, tx, r.RingColors, relId) InsertWithCtxParallel(group, tx, r.RingColors, relId)
InsertWithCtxParallel(group, ctx, tx, r.Subject, relId) InsertWithCtxParallel(group, tx, r.Subject, relId)
err := group.Wait() err := group.Wait()
if err != nil && !errors.Is(err, sql.ErrNoRows) { if err != nil && !errors.Is(err, sql.ErrNoRows) {

View File

@@ -28,10 +28,10 @@ func (s *Setting) Query(data *InsertId[uint]) {
} }
func (s *Setting) InsertObjects(tx *sqlx.Tx) error { func (s *Setting) InsertObjects(tx *sqlx.Tx) error {
group, ctx := errgroup.WithContext(context.Background()) group, _ := errgroup.WithContext(context.Background())
relId := InsertId[string]{id: s.ID, fieldName: "setting_id"} relId := InsertId[string]{id: s.ID, fieldName: "setting_id"}
InsertWithCtxParallel(group, ctx, tx, s.Stats, relId) InsertWithCtxParallel(group, tx, s.Stats, relId)
err := group.Wait() err := group.Wait()
if err != nil && !errors.Is(err, sql.ErrNoRows) { if err != nil && !errors.Is(err, sql.ErrNoRows) {

View File

@@ -32,13 +32,13 @@ type Setup struct {
func (s *Setup) InsertObjects(tx *sqlx.Tx) error { func (s *Setup) InsertObjects(tx *sqlx.Tx) error {
relData := InsertId[uint]{id: s.ID, fieldName: "setup_id"} relData := InsertId[uint]{id: s.ID, fieldName: "setup_id"}
group, ctx := errgroup.WithContext(context.Background()) group, _ := errgroup.WithContext(context.Background())
InsertWithCtxParallel(group, ctx, tx, &s.CoreUpdate, relData) InsertWithCtxParallel(group, tx, &s.CoreUpdate, relData)
InsertWithCtxParallel(group, ctx, tx, &s.FeaturedContent, relData) InsertWithCtxParallel(group, tx, &s.FeaturedContent, relData)
InsertWithCtxParallel(group, ctx, tx, &s.Files, relData) InsertWithCtxParallel(group, tx, &s.Files, relData)
InsertWithCtxParallel(group, ctx, tx, s.Options, relData) InsertWithCtxParallel(group, tx, s.Options, relData)
InsertWithCtxParallel(group, ctx, tx, &s.Release, relData) InsertWithCtxParallel(group, tx, &s.Release, relData)
InsertSliceParallel(group, tx, s.Languages, relData) InsertSliceParallel(group, tx, s.Languages, relData)
InsertSliceParallel(group, tx, s.Modules, relData) InsertSliceParallel(group, tx, s.Modules, relData)

View File

@@ -45,6 +45,10 @@ func (s Stats) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string
args := []any{data.id, s.CoreVersion, s.SystemID, s.SystemVersion, s.LastModifiedBy, s.ModifiedTime} args := []any{data.id, s.CoreVersion, s.SystemID, s.SystemVersion, s.LastModifiedBy, s.ModifiedTime}
mutex := GetMutex("stats_insert")
mutex.Lock()
defer mutex.Unlock()
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&s.ID) err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&s.ID)
if err != nil { if err != nil {
return err return err

View File

@@ -42,6 +42,10 @@ func (s *Style) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[strin
args := []any{data.id, s.Src} args := []any{data.id, s.Src}
mutex := GetMutex("style_insert")
mutex.Lock()
defer mutex.Unlock()
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&s.ID) err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&s.ID)
if err != nil { if err != nil {
return err return err

View File

@@ -64,12 +64,12 @@ func (s *System) ConnectGameQuery(data *InsertId[string]) {
func (s *System) InsertObjects(tx *sqlx.Tx) error { func (s *System) InsertObjects(tx *sqlx.Tx) error {
relId := InsertId[string]{id: s.ID, fieldName: "system_id"} relId := InsertId[string]{id: s.ID, fieldName: "system_id"}
group, ctx := errgroup.WithContext(context.Background()) group, _ := errgroup.WithContext(context.Background())
InsertWithCtxParallel(group, ctx, tx, s.Compatibility, relId) InsertWithCtxParallel(group, tx, s.Compatibility, relId)
InsertWithCtxParallel(group, ctx, tx, s.Relationships, relId) InsertWithCtxParallel(group, tx, s.Relationships, relId)
InsertWithCtxParallel(group, ctx, tx, s.DocumentTypes, relId) InsertWithCtxParallel(group, tx, s.DocumentTypes, relId)
InsertWithCtxParallel(group, ctx, tx, s.Grid, relId) InsertWithCtxParallel(group, tx, s.Grid, relId)
esModulesRelId := &InsertId[string]{id: s.ID, fieldName: "system_id", tableName: "es_modules"} esModulesRelId := &InsertId[string]{id: s.ID, fieldName: "system_id", tableName: "es_modules"}
InsertSimpleSliceParallel(group, tx, s.Esmodules, esModulesRelId) InsertSimpleSliceParallel(group, tx, s.Esmodules, esModulesRelId)

View File

@@ -36,10 +36,10 @@ func (t *Table) Query(data *InsertId[uint]) {
} }
func (t *Table) InsertObjects(tx *sqlx.Tx) error { func (t *Table) InsertObjects(tx *sqlx.Tx) error {
group, ctx := errgroup.WithContext(context.Background()) group, _ := errgroup.WithContext(context.Background())
relId := InsertId[string]{id: t.ID, fieldName: "table_id"} relId := InsertId[string]{id: t.ID, fieldName: "table_id"}
InsertWithCtxParallel(group, ctx, tx, t.Stats, relId) InsertWithCtxParallel(group, tx, t.Stats, relId)
InsertSliceParallel(group, tx, t.Ownership, relId) InsertSliceParallel(group, tx, t.Ownership, relId)
InsertSliceParallel(group, tx, t.Results, relId) InsertSliceParallel(group, tx, t.Results, relId)
@@ -112,9 +112,9 @@ func (t *TableResult) Query(data *InsertId[string]) {
} }
func (t *TableResult) InsertObjects(tx *sqlx.Tx) error { func (t *TableResult) InsertObjects(tx *sqlx.Tx) error {
group, ctx := errgroup.WithContext(context.Background()) group, _ := errgroup.WithContext(context.Background())
InsertWithCtxParallel(group, ctx, tx, t.Stats, InsertId[string]{id: t.ID, fieldName: "table_result_id"}) InsertWithCtxParallel(group, 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"}) InsertSimpleSlice(tx, t.Range, &InsertId[string]{id: t.ID, fieldName: "table_result_id", tableName: "table_result_range"})
err := group.Wait() err := group.Wait()

View File

@@ -45,17 +45,17 @@ func (t *Token) Query(data *InsertId[string]) {
} }
func (t *Token) InsertObjects(tx *sqlx.Tx) error { func (t *Token) InsertObjects(tx *sqlx.Tx) error {
group, ctx := errgroup.WithContext(context.Background()) group, _ := errgroup.WithContext(context.Background())
relId := InsertId[uint]{id: t.ID, fieldName: "token_id"} relId := InsertId[uint]{id: t.ID, fieldName: "token_id"}
InsertWithCtxParallel(group, ctx, tx, t.Ring, relId) InsertWithCtxParallel(group, tx, t.Ring, relId)
InsertWithCtxParallel(group, ctx, tx, t.Sight, relId) InsertWithCtxParallel(group, tx, t.Sight, relId)
InsertWithCtxParallel(group, ctx, tx, t.Texture, relId) InsertWithCtxParallel(group, tx, t.Texture, relId)
InsertWithCtxParallel(group, ctx, tx, t.Bar1, InsertId[uint]{id: t.ID, fieldName: "token_id", tableName: "token_bar_1"}) InsertWithCtxParallel(group, 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, tx, t.Bar1, InsertId[uint]{id: t.ID, fieldName: "token_id", tableName: "token_bar_2"})
InsertWithCtxParallel(group, ctx, tx, t.Light, relId) InsertWithCtxParallel(group, tx, t.Light, relId)
InsertWithCtxParallel(group, ctx, tx, t.Occludable, relId) InsertWithCtxParallel(group, tx, t.Occludable, relId)
InsertWithCtxParallel(group, ctx, tx, t.TurnMarker, relId) InsertWithCtxParallel(group, tx, t.TurnMarker, relId)
err := group.Wait() err := group.Wait()
if err != nil && !errors.Is(err, sql.ErrNoRows) { if err != nil && !errors.Is(err, sql.ErrNoRows) {
@@ -88,6 +88,10 @@ func (t *Token) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[strin
args := []any{data.id, t.Name, t.ActorLink, t.AppendNumber, t.PrependAdjective, t.LockRotation, t.RandomImg, 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} t.DisplayName, t.DisplayBars, t.Disposition, t.Rotation, t.Alpha, t.Width, t.Height}
mutex := GetMutex("token_insert")
mutex.Lock()
defer mutex.Unlock()
err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&t.ID) err := tx.QueryRowxContext(ctx, data.query, args...).Scan(&t.ID)
if err != nil { if err != nil {
return err return err

View File

@@ -33,10 +33,10 @@ func (u *User) Query(data *InsertId[uint]) {
} }
func (u *User) InsertObjects(tx *sqlx.Tx) error { func (u *User) InsertObjects(tx *sqlx.Tx) error {
group, ctx := errgroup.WithContext(context.Background()) group, _ := errgroup.WithContext(context.Background())
relId := InsertId[string]{id: u.ID, fieldName: "user_id"} relId := InsertId[string]{id: u.ID, fieldName: "user_id"}
InsertWithCtxParallel(group, ctx, tx, u.Stats, relId) InsertWithCtxParallel(group, tx, u.Stats, relId)
InsertSliceParallel(group, tx, u.Hotbar, relId) InsertSliceParallel(group, tx, u.Hotbar, relId)
err := group.Wait() err := group.Wait()

View File

@@ -4,6 +4,7 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"sync"
"time" "time"
"github.com/jmoiron/sqlx" "github.com/jmoiron/sqlx"
@@ -41,7 +42,7 @@ func InsertWithCtx[T AllowedIds, I Insertable[T]](tx *sqlx.Tx, data I, relId Ins
return data.InsertCtx(ctx, tx, &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]) { func InsertWithCtxParallel[T AllowedIds, I Insertable[T]](g *errgroup.Group, tx *sqlx.Tx, data I, relId InsertId[T]) {
g.Go(func() error { g.Go(func() error {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel() defer cancel()
@@ -237,3 +238,11 @@ func IsErrUniqueConstraint(err error) {
} }
} }
} }
var modelInsertLock sync.Map
func GetMutex(id string) *sync.Mutex {
val, _ := modelInsertLock.LoadOrStore(id, &sync.Mutex{})
return val.(*sync.Mutex)
}

View File

@@ -61,10 +61,10 @@ func (w *World) Query(data *InsertId[string]) {
func (w *World) InsertObjects(tx *sqlx.Tx) error { func (w *World) InsertObjects(tx *sqlx.Tx) error {
relId := InsertId[string]{id: w.ID, fieldName: "world_id"} relId := InsertId[string]{id: w.ID, fieldName: "world_id"}
group, ctx := errgroup.WithContext(context.Background()) group, _ := errgroup.WithContext(context.Background())
InsertWithCtxParallel(group, ctx, tx, w.Compatibility, relId) InsertWithCtxParallel(group, tx, w.Compatibility, relId)
InsertWithCtxParallel(group, ctx, tx, w.Relationships, relId) InsertWithCtxParallel(group, tx, w.Relationships, relId)
esModulesRelId := &InsertId[string]{id: w.ID, fieldName: "world_id", tableName: "es_modules"} esModulesRelId := &InsertId[string]{id: w.ID, fieldName: "world_id", tableName: "es_modules"}
InsertSimpleSliceParallel(group, tx, w.Esmodules, esModulesRelId) InsertSimpleSliceParallel(group, tx, w.Esmodules, esModulesRelId)

View File

@@ -10,30 +10,28 @@ import (
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/types" "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/types"
) )
func (tr *FoundryTransport) FillDBWithFoundryData() { func (tr *FoundryTransport) FillDBWithFoundryData() error {
now := time.Now() now := time.Now()
setupExist, err := db.HasSetup(tr.DB) setupExist, err := db.HasSetup(tr.DB)
if err != nil { if err != nil {
tr.ReadChan.Err() <- &types.FoundryError{Direction: types.WriterCode, Err: err, Type: types.DbCode} return err
return
} }
if !setupExist { if !setupExist {
err := tr.InsertSetupToDB() err := tr.InsertSetupToDB()
if err != nil { if err != nil {
tr.ReadChan.Err() <- &types.FoundryError{Direction: types.WriterCode, Err: err, Type: types.DbCode} return err
return
} }
} }
err = tr.InitWorldsData() err = tr.InitWorldsData()
if err != nil { if err != nil {
tr.ReadChan.Err() <- &types.FoundryError{Direction: types.WriterCode, Err: err, Type: types.DbCode} return err
return
} }
tr.Logger.Info("Initialization has been successful", "elapsed_time", time.Since(now).String()) tr.Logger.Info("Initialization has been successful", "elapsed_time", time.Since(now).String())
return nil
} }
func (tr *FoundryTransport) InsertSetupToDB() error { func (tr *FoundryTransport) InsertSetupToDB() error {
@@ -66,8 +64,8 @@ func (tr *FoundryTransport) InitWorldsData() error {
for i := range tr.InitWorlds { for i := range tr.InitWorlds {
initWorld := tr.InitWorlds[i] initWorld := tr.InitWorlds[i]
if !slices.Contains(worldNames, initWorld) { if !slices.Contains(worldNames, initWorld.Name) {
tr.Logger.Warn("World name does not exist in database or have been already inserted. Skip!", "world_name", initWorld) tr.Logger.Warn("World name does not exist in database or have been already inserted. Skip!", "world_name", initWorld.Name)
continue continue
} }
@@ -84,14 +82,21 @@ func (tr *FoundryTransport) InitWorldsData() error {
return nil return nil
} }
func (tr *FoundryTransport) InitWorldData(worldName string) error { func (tr *FoundryTransport) InitWorldData(worldData types.WorldData) error {
tr.Logger.Debug("Launching world...", "world_name", worldName) tr.Logger.Debug("Launching world...", "world_name", worldData.Name)
err := tr.LaunchWorld(worldName) err := tr.LaunchWorld(worldData.Name)
if err != nil { if err != nil {
return err return err
} }
err = tr.ConnectToWorld() if worldData.UserId == "" {
worldData.UserId, err = tr.GetUserId(worldData.Username)
if err != nil {
return err
}
}
err = tr.ConnectToWorld(worldData.UserId, worldData.UserPass)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -10,4 +10,5 @@ var (
ErrorAuthPassWrong = errors.New("Authentication password is wrong") ErrorAuthPassWrong = errors.New("Authentication password is wrong")
ErrorChannelIsClosed = errors.New("Channel has been closed") ErrorChannelIsClosed = errors.New("Channel has been closed")
ErrorUserIsNotConnected = errors.New("User is not connected") ErrorUserIsNotConnected = errors.New("User is not connected")
ErrorUserDoesntExist = errors.New("User does not exist")
) )

View File

@@ -85,7 +85,7 @@ func (tr *FoundryTransport) CheckAuthResponse(body io.Reader, respLength int) er
return nil return nil
} }
func (tr *FoundryTransport) LogInToWorld() error { func (tr *FoundryTransport) LogInToWorld(userId string, userPass string) error {
if tr.Http.SessionID == nil { if tr.Http.SessionID == nil {
err := tr.Http.GetSessionId() err := tr.Http.GetSessionId()
if err != nil { if err != nil {
@@ -93,8 +93,7 @@ func (tr *FoundryTransport) LogInToWorld() error {
} }
} }
// TODO: make userid and password from tr object resp, err := tr.Http.PostJoinWorld(userId, userPass)
resp, err := tr.Http.PostJoinWorld("YpMHZge0dxBZS5Cm", "")
if err != nil { if err != nil {
return err return err
} }
@@ -106,8 +105,6 @@ func (tr *FoundryTransport) LogInToWorld() error {
} }
return tr.CheckLoginResponse(resp.Body, contentLen) return tr.CheckLoginResponse(resp.Body, contentLen)
// {"userid":"YpMHZge0dxBZS5Cm","password":"","action":"join"}
} }
func (tr *FoundryTransport) CheckLoginResponse(body io.Reader, respLength int) error { func (tr *FoundryTransport) CheckLoginResponse(body io.Reader, respLength int) error {

View File

@@ -29,7 +29,7 @@ type FoundryTransport struct {
// Models *db.Models // Models *db.Models
DB *sqlx.DB DB *sqlx.DB
IsDbInit bool IsDbInit bool
InitWorlds []string InitWorlds []types.WorldData
Logger *slog.Logger Logger *slog.Logger
} }
@@ -38,7 +38,7 @@ type FoundryTransportData struct {
DbConn *sqlx.DB DbConn *sqlx.DB
Logger *slog.Logger Logger *slog.Logger
HttpConfig *requests.FoundryHttpRequest HttpConfig *requests.FoundryHttpRequest
Worlds []string Worlds []types.WorldData
} }
func NewFoundryTransport(data *FoundryTransportData) *FoundryTransport { func NewFoundryTransport(data *FoundryTransportData) *FoundryTransport {
@@ -64,25 +64,26 @@ func NewFoundryTransport(data *FoundryTransportData) *FoundryTransport {
} }
} }
func (t *FoundryTransport) CloseMsgChannel(id int, timeout time.Duration) bool { func (t *FoundryTransport) CloseMsgChannel(msgChan chan []byte, id int, timeout time.Duration) bool {
time.Sleep(timeout) time.Sleep(timeout)
t.ChanMutex.Lock() t.ChanMutex.Lock()
defer t.ChanMutex.Unlock() defer t.ChanMutex.Unlock()
ok := true ok := true
if _, ok = t.ExchangeChan.Msgs[id]; !ok {
return false
}
select { select {
case _, ok = <-t.ExchangeChan.Msgs[id]: case _, ok = <-msgChan:
if ok { if ok {
close(t.ExchangeChan.Msgs[id]) if msgChan == t.ExchangeChan.Msgs[id] {
delete(t.ExchangeChan.Msgs, id) delete(t.ExchangeChan.Msgs, id)
}
close(msgChan)
} }
default: default:
close(t.ExchangeChan.Msgs[id]) if msgChan == t.ExchangeChan.Msgs[id] {
delete(t.ExchangeChan.Msgs, id) delete(t.ExchangeChan.Msgs, id)
}
close(msgChan)
} }
return ok return ok

View File

@@ -1,11 +1,15 @@
package transport package transport
import ( import (
"context"
"encoding/json"
"fmt" "fmt"
"net/http" "net/http"
"net/url" "net/url"
"strings"
"time" "time"
json_models "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/json"
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/requests" "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/requests"
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/types" "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/types"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
@@ -58,8 +62,8 @@ func (tr *FoundryTransport) HandleWebsocketRequest(msg *types.WsMessage) ([]byte
} }
func (tr *FoundryTransport) ReceiveMessage(id int) ([]byte, error) { func (tr *FoundryTransport) ReceiveMessage(id int) ([]byte, error) {
// ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
// defer cancel() defer cancel()
for { for {
select { select {
@@ -68,14 +72,14 @@ func (tr *FoundryTransport) ReceiveMessage(id int) ([]byte, error) {
time.Sleep(5 * time.Microsecond) time.Sleep(5 * time.Microsecond)
continue continue
} }
tr.CloseMsgChannel(id, 0) tr.CloseMsgChannel(tr.ExchangeChan.Msgs[id], id, 0)
return msg, nil return msg, nil
// case <-ctx.Done(): case <-ctx.Done():
// err := ctx.Err() err := ctx.Err()
// if err != nil { if err != nil {
// return nil, err return nil, err
// } }
// return nil, ErrorTimeout return nil, ErrorTimeout
default: default:
time.Sleep(5 * time.Microsecond) time.Sleep(5 * time.Microsecond)
} }
@@ -105,8 +109,8 @@ func (tr *FoundryTransport) ListenWebSocket() {
} }
} }
func (tr *FoundryTransport) ConnectToWorld() error { func (tr *FoundryTransport) ConnectToWorld(userId string, userPass string) error {
err := tr.LogInToWorld() err := tr.LogInToWorld(userId, userPass)
if err != nil { if err != nil {
return err return err
} }
@@ -126,6 +130,58 @@ func (tr *FoundryTransport) ConnectToWorld() error {
return nil return nil
} }
func (tr *FoundryTransport) GetUserId(username string) (string, error) {
msgJson, err := tr.GetJsonDataByType(requests.JoinPath)
if err != nil {
return "", err
}
var modelUsers []struct {
Users []json_models.User `json:"users"`
}
err = json.Unmarshal(msgJson, &modelUsers)
if err != nil {
return "", err
}
tr.Logger.Debug("User data", "msg in json format", modelUsers)
users := modelUsers[0].Users
for i := range users {
if strings.EqualFold(users[i].Name, username) {
return users[i].ID, nil
}
}
return "", ErrorUserDoesntExist
}
func (tr *FoundryTransport) GetUserIdAndPass() (string, string, error) {
status, err := tr.Http.GetStatus()
if err != nil {
return "", "", err
}
var worldData *types.WorldData
for i := range tr.InitWorlds {
if strings.EqualFold(tr.InitWorlds[i].Name, status.World) {
worldData = &tr.InitWorlds[i]
break
}
}
if worldData == nil {
return "", "", err
}
userId, err := tr.GetUserId(worldData.Username)
if err != nil {
return "", "", err
}
return userId, worldData.UserPass, nil
}
func (tr *FoundryTransport) SendOnlyCodeRequest(code string) error { func (tr *FoundryTransport) SendOnlyCodeRequest(code string) error {
tr.Logger.Debug("WS: Data has been send\n", "msg", types.CodesRespToReq[code]) tr.Logger.Debug("WS: Data has been send\n", "msg", types.CodesRespToReq[code])

View File

@@ -0,0 +1,50 @@
package types
import (
"errors"
"strings"
)
var (
ErrNoAuthWorldData = errors.New("You shold pass world data for initialization")
ErrWrongFormat = errors.New("You have passed data in wrong format. Please check it once again")
)
type WorldData struct {
Name string
Username string
UserId string
UserPass string
}
func CreateWorldDataSlice(worldFlag string, userFlag string, passFlag string) ([]WorldData, error) {
worlds := strings.Split(worldFlag, ",")
users := strings.Split(userFlag, ",")
passwords := strings.Split(passFlag, ",")
if (len(worlds) == 1 && worlds[0] == "") || (len(users) == 1 && users[0] == "") {
return nil, ErrNoAuthWorldData
}
worldLen := len(worlds)
userLen := len(users)
passLen := len(passwords)
worldData := make([]WorldData, worldLen)
if worldLen == userLen && userLen == passLen {
for i := range worlds {
worldData[i].Name = worlds[i]
worldData[i].Username = users[i]
worldData[i].UserPass = passwords[i]
}
} else if worldLen != userLen && userLen == passLen && passLen == 1 {
for i := range worlds {
worldData[i].Name = worlds[i]
worldData[i].Username = users[0]
worldData[i].UserPass = passwords[0]
}
} else {
return nil, ErrWrongFormat
}
return worldData, nil
}