From 44ce68400781f06d2e5c6843ca18669c923e3f15 Mon Sep 17 00:00:00 2001 From: John Chadwick Date: Sun, 25 Jun 2023 11:57:37 -0400 Subject: [PATCH] Substantial game server update + DB break --- cmd/gameserver/main.go | 2 + cmd/loginserver/main.go | 2 + common/server.go | 6 +- database/accounts/service.go | 703 +++++++++++++++++++- game/model/room.go | 13 +- game/packet/client.go | 70 +- game/packet/server.go | 147 +++- game/room/event.go | 9 + game/room/room.go | 9 +- game/server/auth.go | 183 +++-- game/server/conn.go | 220 +++++- game/server/playerdata.go | 112 ++++ game/server/server.go | 4 + gameconfig/config.go | 90 +++ gameconfig/default.json | 4 + gen.go | 4 +- gen/dbmodels/character.sql.go | 709 +++++++++++++++++++- gen/dbmodels/inventory.sql.go | 168 +++++ gen/dbmodels/models.go | 95 ++- gen/dbmodels/player.sql.go | 738 ++++++++++++++++++++- login/conn.go | 42 +- login/server.go | 7 +- migrations/0001_create_player_table.sql | 12 - migrations/0001_initial.sql | 130 ++++ migrations/0002_create_character_table.sql | 10 - migrations/0003_create_sessions_table.sql | 15 - minibox/gameserver.go | 2 + minibox/loginserver.go | 2 + pangya/player.go | 15 +- queries/character.sql | 143 +++- queries/inventory.sql | 26 + queries/player.sql | 95 ++- sqlc.sh | 5 + 33 files changed, 3566 insertions(+), 226 deletions(-) create mode 100644 game/server/playerdata.go create mode 100644 gameconfig/config.go create mode 100644 gameconfig/default.json create mode 100644 gen/dbmodels/inventory.sql.go delete mode 100644 migrations/0001_create_player_table.sql create mode 100644 migrations/0001_initial.sql delete mode 100644 migrations/0002_create_character_table.sql delete mode 100644 migrations/0003_create_sessions_table.sql create mode 100644 queries/inventory.sql create mode 100755 sqlc.sh diff --git a/cmd/gameserver/main.go b/cmd/gameserver/main.go index b57287f..a04e39c 100644 --- a/cmd/gameserver/main.go +++ b/cmd/gameserver/main.go @@ -26,6 +26,7 @@ import ( "github.com/pangbox/server/database" "github.com/pangbox/server/database/accounts" gameserver "github.com/pangbox/server/game/server" + "github.com/pangbox/server/gameconfig" log "github.com/sirupsen/logrus" "github.com/xo/dburl" ) @@ -72,6 +73,7 @@ func main() { Database: db, Hasher: hash.Bcrypt{}, }), + ConfigProvider: gameconfig.Default(), }) log.Fatalln(gameServer.Listen(ctx, listenAddr)) } diff --git a/cmd/loginserver/main.go b/cmd/loginserver/main.go index 66fc65e..369da75 100644 --- a/cmd/loginserver/main.go +++ b/cmd/loginserver/main.go @@ -25,6 +25,7 @@ import ( "github.com/pangbox/server/common/topology" "github.com/pangbox/server/database" "github.com/pangbox/server/database/accounts" + "github.com/pangbox/server/gameconfig" "github.com/pangbox/server/login" log "github.com/sirupsen/logrus" "github.com/xo/dburl" @@ -72,6 +73,7 @@ func main() { Database: db, Hasher: hash.Bcrypt{}, }), + ConfigProvider: gameconfig.Default(), }) log.Fatalln(loginServer.Listen(ctx, listenAddr)) } diff --git a/common/server.go b/common/server.go index 6c66d97..ae1aec4 100755 --- a/common/server.go +++ b/common/server.go @@ -66,9 +66,11 @@ func (b *BaseServer) Listen(logger *log.Entry, addr string, handler BaseHandlerF logger.WithField("error", r).Error("PANIC in connection") } logger.Info("Exiting connection thread.") + if err := socket.Close(); err != nil { + logger.WithError(err).Warning("error closing socket") + } }() - err := handler(logger, socket) - if err != nil { + if err := handler(logger, socket); err != nil { logger.WithError(err).Error("ERROR in connection") } }() diff --git a/database/accounts/service.go b/database/accounts/service.go index c663b7f..aa085ec 100755 --- a/database/accounts/service.go +++ b/database/accounts/service.go @@ -20,11 +20,10 @@ package accounts import ( "context" "database/sql" - "encoding/binary" "errors" + "fmt" "time" - "github.com/go-restruct/restruct" "github.com/google/uuid" "github.com/pangbox/server/common/hash" "github.com/pangbox/server/gen/dbmodels" @@ -41,26 +40,28 @@ const sessionTimeout = 15 * time.Minute // Options specifies options for account services. type Options struct { - Database dbmodels.DBTX + Database *sql.DB Hasher hash.Hasher } // Service implements account services using the database. type Service struct { - database *dbmodels.Queries - hasher hash.Hasher + db *sql.DB + queries *dbmodels.Queries + hasher hash.Hasher } // NewService creates new account services using the database. func NewService(opts Options) *Service { return &Service{ - database: dbmodels.New(opts.Database), - hasher: opts.Hasher, + db: opts.Database, + queries: dbmodels.New(opts.Database), + hasher: opts.Hasher, } } -func (s *Service) GetPlayer(ctx context.Context, playerID int64) (dbmodels.Player, error) { - return s.database.GetPlayer(ctx, playerID) +func (s *Service) GetPlayer(ctx context.Context, playerID int64) (dbmodels.GetPlayerRow, error) { + return s.queries.GetPlayer(ctx, playerID) } func (s *Service) Register(ctx context.Context, username, password string) (dbmodels.Player, error) { @@ -68,7 +69,7 @@ func (s *Service) Register(ctx context.Context, username, password string) (dbmo if err != nil { return dbmodels.Player{}, err } - return s.database.CreatePlayer(ctx, dbmodels.CreatePlayerParams{ + return s.queries.CreatePlayer(ctx, dbmodels.CreatePlayerParams{ Username: username, PasswordHash: hash, }) @@ -76,7 +77,7 @@ func (s *Service) Register(ctx context.Context, username, password string) (dbmo // Authenticate authenticates a user using the database. func (s *Service) Authenticate(ctx context.Context, username, password string) (dbmodels.Player, error) { - player, err := s.database.GetPlayerByUsername(ctx, username) + player, err := s.queries.GetPlayerByUsername(ctx, username) if errors.Is(err, sql.ErrNoRows) { return dbmodels.Player{}, ErrUnknownUsername } else if err != nil { @@ -90,7 +91,7 @@ func (s *Service) Authenticate(ctx context.Context, username, password string) ( // SetNickname sets the player's nickname. func (s *Service) SetNickname(ctx context.Context, playerID int64, nickname string) (dbmodels.Player, error) { - return s.database.SetPlayerNickname(ctx, dbmodels.SetPlayerNicknameParams{ + return s.queries.SetPlayerNickname(ctx, dbmodels.SetPlayerNicknameParams{ PlayerID: playerID, Nickname: sql.NullString{Valid: true, String: nickname}, }) @@ -98,46 +99,690 @@ func (s *Service) SetNickname(ctx context.Context, playerID int64, nickname stri // HasCharacters returns whether or not the player has characters. func (s *Service) HasCharacters(ctx context.Context, playerID int64) (bool, error) { - return s.database.PlayerHasCharacters(ctx, playerID) + return s.queries.PlayerHasCharacters(ctx, playerID) } // GetCharacters returns the characters for a given player. func (s *Service) GetCharacters(ctx context.Context, playerID int64) ([]pangya.PlayerCharacterData, error) { - characters, err := s.database.GetCharactersByPlayer(ctx, playerID) + characters, err := s.queries.GetCharactersByPlayer(ctx, playerID) if err != nil { return nil, err } result := make([]pangya.PlayerCharacterData, len(characters)) for i, character := range characters { - if err := restruct.Unpack(character.CharacterData, binary.LittleEndian, &result[i]); err != nil { - return nil, err + result[i] = pangya.PlayerCharacterData{ + CharTypeID: uint32(character.CharacterTypeID), + ID: uint32(character.CharacterID), + HairColor: uint8(character.HairColor), + Shirt: uint8(character.Shirt), + PartTypeIDs: [24]uint32{ + uint32(character.Part00ItemTypeID), uint32(character.Part01ItemTypeID), uint32(character.Part02ItemTypeID), uint32(character.Part03ItemTypeID), + uint32(character.Part04ItemTypeID), uint32(character.Part05ItemTypeID), uint32(character.Part06ItemTypeID), uint32(character.Part07ItemTypeID), + uint32(character.Part08ItemTypeID), uint32(character.Part09ItemTypeID), uint32(character.Part10ItemTypeID), uint32(character.Part11ItemTypeID), + uint32(character.Part12ItemTypeID), uint32(character.Part13ItemTypeID), uint32(character.Part14ItemTypeID), uint32(character.Part15ItemTypeID), + uint32(character.Part16ItemTypeID), uint32(character.Part17ItemTypeID), uint32(character.Part18ItemTypeID), uint32(character.Part19ItemTypeID), + uint32(character.Part20ItemTypeID), uint32(character.Part21ItemTypeID), uint32(character.Part22ItemTypeID), uint32(character.Part23ItemTypeID), + }, + PartIDs: [24]uint32{ + uint32(character.Part00ItemID.Int64), uint32(character.Part01ItemID.Int64), uint32(character.Part02ItemID.Int64), uint32(character.Part03ItemID.Int64), + uint32(character.Part04ItemID.Int64), uint32(character.Part05ItemID.Int64), uint32(character.Part06ItemID.Int64), uint32(character.Part07ItemID.Int64), + uint32(character.Part08ItemID.Int64), uint32(character.Part09ItemID.Int64), uint32(character.Part10ItemID.Int64), uint32(character.Part11ItemID.Int64), + uint32(character.Part12ItemID.Int64), uint32(character.Part13ItemID.Int64), uint32(character.Part14ItemID.Int64), uint32(character.Part15ItemID.Int64), + uint32(character.Part16ItemID.Int64), uint32(character.Part17ItemID.Int64), uint32(character.Part18ItemID.Int64), uint32(character.Part19ItemID.Int64), + uint32(character.Part20ItemID.Int64), uint32(character.Part21ItemID.Int64), uint32(character.Part22ItemID.Int64), uint32(character.Part23ItemID.Int64), + }, + AuxParts: [5]uint32{ + uint32(character.AuxPart0ID.Int64), + uint32(character.AuxPart1ID.Int64), + uint32(character.AuxPart2ID.Int64), + uint32(character.AuxPart3ID.Int64), + uint32(character.AuxPart4ID.Int64), + }, + CutInID: uint32(character.CutInID.Int64), + //Mastery: uint32(character.Mastery), } result[i].ID = uint32(character.CharacterID) } return result, nil } +type NewCharacterParams struct { + CharTypeID uint32 + HairColor uint8 + Shirt uint8 + Mastery uint32 + DefaultPartTypeIDs [24]uint32 +} + // AddCharacter adds a character for a given player. -func (s *Service) AddCharacter(ctx context.Context, playerID int64, data pangya.PlayerCharacterData) error { - blob, err := restruct.Pack(binary.LittleEndian, &data) +func (s *Service) AddCharacter(ctx context.Context, playerID int64, params NewCharacterParams) (dbmodels.Character, error) { + tx, err := s.db.BeginTx(ctx, nil) + if err != nil { + return dbmodels.Character{}, err + } + defer tx.Rollback() + + queries := s.queries.WithTx(tx) + + item, err := queries.AddItemToInventory(ctx, dbmodels.AddItemToInventoryParams{ + PlayerID: playerID, + ItemTypeID: int64(params.CharTypeID), + }) + if err != nil { + return dbmodels.Character{}, err + } + + character, err := queries.CreateCharacter(ctx, dbmodels.CreateCharacterParams{ + PlayerID: playerID, + ItemID: item.ItemID, + HairColor: int64(params.HairColor), + Shirt: int64(params.Shirt), + Mastery: int64(params.Mastery), + Part00ItemTypeID: int64(params.DefaultPartTypeIDs[0]), + Part01ItemTypeID: int64(params.DefaultPartTypeIDs[1]), + Part02ItemTypeID: int64(params.DefaultPartTypeIDs[2]), + Part03ItemTypeID: int64(params.DefaultPartTypeIDs[3]), + Part04ItemTypeID: int64(params.DefaultPartTypeIDs[4]), + Part05ItemTypeID: int64(params.DefaultPartTypeIDs[5]), + Part06ItemTypeID: int64(params.DefaultPartTypeIDs[6]), + Part07ItemTypeID: int64(params.DefaultPartTypeIDs[7]), + Part08ItemTypeID: int64(params.DefaultPartTypeIDs[8]), + Part09ItemTypeID: int64(params.DefaultPartTypeIDs[9]), + Part10ItemTypeID: int64(params.DefaultPartTypeIDs[10]), + Part11ItemTypeID: int64(params.DefaultPartTypeIDs[11]), + Part12ItemTypeID: int64(params.DefaultPartTypeIDs[12]), + Part13ItemTypeID: int64(params.DefaultPartTypeIDs[13]), + Part14ItemTypeID: int64(params.DefaultPartTypeIDs[14]), + Part15ItemTypeID: int64(params.DefaultPartTypeIDs[15]), + Part16ItemTypeID: int64(params.DefaultPartTypeIDs[16]), + Part17ItemTypeID: int64(params.DefaultPartTypeIDs[17]), + Part18ItemTypeID: int64(params.DefaultPartTypeIDs[18]), + Part19ItemTypeID: int64(params.DefaultPartTypeIDs[19]), + Part20ItemTypeID: int64(params.DefaultPartTypeIDs[20]), + Part21ItemTypeID: int64(params.DefaultPartTypeIDs[21]), + Part22ItemTypeID: int64(params.DefaultPartTypeIDs[22]), + Part23ItemTypeID: int64(params.DefaultPartTypeIDs[23]), + }) + + if err != nil { + return dbmodels.Character{}, err + } + + err = tx.Commit() + if err != nil { + return dbmodels.Character{}, err + } + + return character, nil +} + +func (s *Service) AddClubSet(ctx context.Context, playerID int64, clubsetTypeID uint32) (dbmodels.Inventory, error) { + return s.queries.AddItemToInventory(ctx, dbmodels.AddItemToInventoryParams{ + PlayerID: playerID, + ItemTypeID: int64(clubsetTypeID), + }) +} + +func (s *Service) SetCharacter(ctx context.Context, playerID int64, characterID int64) error { + _, err := s.queries.SetPlayerCharacter(ctx, dbmodels.SetPlayerCharacterParams{ + PlayerID: playerID, + CharacterID: sql.NullInt64{Valid: true, Int64: characterID}, + }) + return err +} + +func (s *Service) SetCaddie(ctx context.Context, playerID int64, caddieID int64) error { + _, err := s.queries.SetPlayerCaddie(ctx, dbmodels.SetPlayerCaddieParams{ + PlayerID: playerID, + CaddieID: sql.NullInt64{Valid: true, Int64: caddieID}, + }) + if err != nil { + return err + } + return nil +} + +func (s *Service) getConsumablesWith(ctx context.Context, tx *dbmodels.Queries, playerID int64) ([10]uint32, error) { + row, err := tx.GetPlayerConsumables(ctx, playerID) + if err != nil { + return [10]uint32{}, err + } + return [10]uint32{ + uint32(row.Slot0TypeID), + uint32(row.Slot1TypeID), + uint32(row.Slot2TypeID), + uint32(row.Slot3TypeID), + uint32(row.Slot4TypeID), + uint32(row.Slot5TypeID), + uint32(row.Slot6TypeID), + uint32(row.Slot7TypeID), + uint32(row.Slot8TypeID), + uint32(row.Slot9TypeID), + }, nil +} + +func (s *Service) decrementConsumableQuantityWith(ctx context.Context, tx *dbmodels.Queries, playerID, itemTypeID int64) (int64, error) { + items, err := tx.GetItemsByTypeID(ctx, dbmodels.GetItemsByTypeIDParams{ + PlayerID: playerID, + ItemTypeID: itemTypeID, + }) + if err != nil { + return 0, err + } + if len(items) == 0 { + return 0, fmt.Errorf("item %08x not in inventory", itemTypeID) + } else if len(items) != 1 { + return 0, errors.New("cardinality error") + } + item := items[0] + quantity := item.Quantity.Int64 + if !item.Quantity.Valid || quantity == 0 { + // Items without quantities are not consumable and can't be equipped here. + return 0, errors.New("invalid item quantity in inventory") + } else if quantity > 1 { + _, err := tx.SetItemQuantity(ctx, dbmodels.SetItemQuantityParams{ + PlayerID: playerID, + Quantity: sql.NullInt64{Int64: quantity - 1, Valid: true}, + }) + if err != nil { + return 0, err + } + return item.ItemID, nil + } else { + return item.ItemID, tx.RemoveItemFromInventory(ctx, dbmodels.RemoveItemFromInventoryParams{ + PlayerID: playerID, + ItemID: item.ItemID, + }) + } +} + +func (s *Service) incrementConsumableQuantityWith(ctx context.Context, tx *dbmodels.Queries, playerID, itemTypeID, amount int64) error { + items, err := tx.GetItemsByTypeID(ctx, dbmodels.GetItemsByTypeIDParams{ + PlayerID: playerID, + ItemTypeID: itemTypeID, + }) if err != nil { return err } - _, err = s.database.CreateCharacter(ctx, dbmodels.CreateCharacterParams{ - PlayerID: playerID, - CharacterTypeID: int64(data.CharTypeID), - CharacterData: blob, + if len(items) > 1 { + return errors.New("cardinality error") + } else if len(items) == 0 { + _, err = tx.AddItemToInventory(ctx, dbmodels.AddItemToInventoryParams{ + PlayerID: playerID, + ItemTypeID: itemTypeID, + Quantity: sql.NullInt64{Valid: true, Int64: 1}, + }) + if err != nil { + return fmt.Errorf("adding item to inventory: %w", err) + } + return nil + } + item := items[0] + if !item.Quantity.Valid || item.Quantity.Int64 == 0 { + // Items without quantities are not consumable and can't be equipped here. + return errors.New("invalid item quantity in inventory") + } + quantity := item.Quantity.Int64 + _, err = tx.SetItemQuantity(ctx, dbmodels.SetItemQuantityParams{ + PlayerID: playerID, + Quantity: sql.NullInt64{Int64: quantity + amount, Valid: true}, }) return err } +func (s *Service) GetConsumables(ctx context.Context, playerID int64) ([10]uint32, error) { + return s.getConsumablesWith(ctx, s.queries, playerID) +} + +func (s *Service) SetConsumables(ctx context.Context, playerID int64, newSlots [10]uint32, player *dbmodels.GetPlayerRow) error { + tx, err := s.db.BeginTx(ctx, nil) + if err != nil { + return err + } + defer tx.Rollback() + + queries := s.queries.WithTx(tx) + oldSlots, err := s.getConsumablesWith(ctx, queries, playerID) + if err != nil { + return err + } + + // Inefficient, but should be OK for just 10 item slots. + for i := range newSlots { + // Only act if the old slot != the new slot. + if oldSlots[i] != newSlots[i] { + // If the old slot had something, increment it back to the inventory. + if oldSlots[i] != 0 { + if err := s.incrementConsumableQuantityWith(ctx, queries, playerID, int64(oldSlots[i]), 1); err != nil { + return err + } + } + + // If the new slot has something, decrement it from the inventory. + if newSlots[i] != 0 { + if _, err := s.decrementConsumableQuantityWith(ctx, queries, playerID, int64(newSlots[i])); err != nil { + return fmt.Errorf("set consumables: %w", err) + } + } + } + } + + ret, err := queries.SetPlayerConsumables(ctx, dbmodels.SetPlayerConsumablesParams{ + PlayerID: playerID, + Slot0TypeID: int64(newSlots[0]), + Slot1TypeID: int64(newSlots[1]), + Slot2TypeID: int64(newSlots[2]), + Slot3TypeID: int64(newSlots[3]), + Slot4TypeID: int64(newSlots[4]), + Slot5TypeID: int64(newSlots[5]), + Slot6TypeID: int64(newSlots[6]), + Slot7TypeID: int64(newSlots[7]), + Slot8TypeID: int64(newSlots[8]), + Slot9TypeID: int64(newSlots[9]), + }) + if err != nil { + return err + } + + err = tx.Commit() + if err != nil { + return err + } + + if player != nil { + player.Slot0TypeID = ret.Slot0TypeID + player.Slot1TypeID = ret.Slot1TypeID + player.Slot2TypeID = ret.Slot2TypeID + player.Slot3TypeID = ret.Slot3TypeID + player.Slot4TypeID = ret.Slot4TypeID + player.Slot5TypeID = ret.Slot5TypeID + player.Slot6TypeID = ret.Slot6TypeID + player.Slot7TypeID = ret.Slot7TypeID + player.Slot8TypeID = ret.Slot8TypeID + player.Slot9TypeID = ret.Slot9TypeID + } + + return nil +} + +func (s *Service) SetComet(ctx context.Context, playerID int64, cometTypeID uint32, player *dbmodels.GetPlayerRow) (int64, error) { + tx, err := s.db.BeginTx(ctx, nil) + if err != nil { + return 0, err + } + defer tx.Rollback() + + queries := s.queries.WithTx(tx) + + cometID := int64(0) + if cometTypeID != 0 { + cometID, err = s.decrementConsumableQuantityWith(ctx, queries, playerID, int64(cometTypeID)) + if err != nil { + return 0, fmt.Errorf("set comet: %w", err) + } + } + + ret, err := queries.SetPlayerComet(ctx, dbmodels.SetPlayerCometParams{ + PlayerID: playerID, + BallTypeID: int64(cometTypeID), + }) + if err != nil { + return 0, err + } + + err = tx.Commit() + if err != nil { + return 0, err + } + + if player != nil { + player.BallTypeID = ret.BallTypeID + } + + return cometID, nil +} + +func (s *Service) SetClubSet(ctx context.Context, playerID int64, clubsetID int64) error { + _, err := s.queries.SetPlayerClubSet(ctx, dbmodels.SetPlayerClubSetParams{ + PlayerID: playerID, + ClubID: sql.NullInt64{Valid: true, Int64: clubsetID}, + }) + if err != nil { + return err + } + + return nil +} + +func (s *Service) PurchaseItem(ctx context.Context, playerID, pangTotal, pointTotal, itemTypeID, quantity int64) (dbmodels.SetPlayerCurrencyRow, error) { + tx, err := s.db.BeginTx(ctx, nil) + if err != nil { + return dbmodels.SetPlayerCurrencyRow{}, err + } + defer tx.Rollback() + + queries := s.queries.WithTx(tx) + + currency, err := queries.GetPlayerCurrency(ctx, playerID) + if err != nil { + return dbmodels.SetPlayerCurrencyRow{}, fmt.Errorf("getting player currency: %w", err) + } + + if quantity != 0 { + if err := s.incrementConsumableQuantityWith(ctx, queries, playerID, itemTypeID, quantity); err != nil { + return dbmodels.SetPlayerCurrencyRow{}, fmt.Errorf("adding consumable quantity to inventory: %w", err) + } + } else { + item, err := queries.AddItemToInventory(ctx, dbmodels.AddItemToInventoryParams{ + PlayerID: playerID, + ItemTypeID: itemTypeID, + }) + if err != nil { + return dbmodels.SetPlayerCurrencyRow{}, fmt.Errorf("adding item to inventory: %w", err) + } + // TODO: should use IFF data + if itemTypeID >= 0x4000000 && itemTypeID < 0x40000FF { + if _, err := queries.CreateCharacter(ctx, dbmodels.CreateCharacterParams{ + PlayerID: playerID, + ItemID: item.ItemID, + }); err != nil { + return dbmodels.SetPlayerCurrencyRow{}, fmt.Errorf("creating character entry: %w", err) + } + } + } + + newCurrency, err := queries.SetPlayerCurrency(ctx, dbmodels.SetPlayerCurrencyParams{ + PlayerID: playerID, + Pang: currency.Pang - pangTotal, + Points: currency.Points - pointTotal, + }) + if err != nil { + return dbmodels.SetPlayerCurrencyRow{}, fmt.Errorf("setting player currency: %w", err) + } + + err = tx.Commit() + if err != nil { + return dbmodels.SetPlayerCurrencyRow{}, err + } + + return newCurrency, nil +} + +type DecorationTypeIDs struct { + BackgroundTypeID uint32 + FrameTypeID uint32 + StickerTypeID uint32 + SlotTypeID uint32 + CutInTypeID uint32 + TitleTypeID uint32 +} + +func (s *Service) getDecorationIDWith(ctx context.Context, tx *dbmodels.Queries, playerID int64, typeID uint32) (sql.NullInt64, error) { + if typeID == 0 { + return sql.NullInt64{}, nil + } + items, err := tx.GetItemsByTypeID(ctx, dbmodels.GetItemsByTypeIDParams{ + PlayerID: playerID, + ItemTypeID: int64(typeID), + }) + if err != nil { + return sql.NullInt64{}, err + } + if len(items) == 0 { + return sql.NullInt64{}, nil + } + return sql.NullInt64{Valid: true, Int64: items[0].ItemID}, nil +} + +func (s *Service) SetDecoration(ctx context.Context, playerId int64, decoration DecorationTypeIDs) error { + tx, err := s.db.BeginTx(ctx, nil) + if err != nil { + return err + } + defer tx.Rollback() + + queries := s.queries.WithTx(tx) + + backgroundID, err := s.getDecorationIDWith(ctx, queries, playerId, decoration.BackgroundTypeID) + if err != nil { + return err + } + + frameID, err := s.getDecorationIDWith(ctx, queries, playerId, decoration.FrameTypeID) + if err != nil { + return err + } + + stickerID, err := s.getDecorationIDWith(ctx, queries, playerId, decoration.StickerTypeID) + if err != nil { + return err + } + + slotID, err := s.getDecorationIDWith(ctx, queries, playerId, decoration.SlotTypeID) + if err != nil { + return err + } + + cutInID, err := s.getDecorationIDWith(ctx, queries, playerId, decoration.CutInTypeID) + if err != nil { + return err + } + + titleID, err := s.getDecorationIDWith(ctx, queries, playerId, decoration.TitleTypeID) + if err != nil { + return err + } + + queries.SetPlayerDecoration(ctx, dbmodels.SetPlayerDecorationParams{ + BackgroundID: backgroundID, + FrameID: frameID, + StickerID: stickerID, + SlotID: slotID, + CutInID: cutInID, + TitleID: titleID, + }) + + err = tx.Commit() + if err != nil { + return err + } + + return nil +} + +func (s *Service) getPartIDWith(ctx context.Context, tx *dbmodels.Queries, playerID int64, itemID uint32) (sql.NullInt64, error) { + if itemID == 0 { + return sql.NullInt64{}, nil + } + item, err := tx.GetItem(ctx, dbmodels.GetItemParams{ + PlayerID: playerID, + ItemID: int64(itemID), + }) + if err != nil { + return sql.NullInt64{}, err + } + return sql.NullInt64{Valid: true, Int64: item.ItemID}, nil +} + +func (s *Service) SetCharacterParts(ctx context.Context, playerID int64, data pangya.PlayerCharacterData) error { + tx, err := s.db.BeginTx(ctx, nil) + if err != nil { + return err + } + defer tx.Rollback() + + queries := s.queries.WithTx(tx) + + part00Item, err := s.getPartIDWith(ctx, queries, playerID, data.PartIDs[0]) + if err != nil { + return err + } + part01Item, err := s.getPartIDWith(ctx, queries, playerID, data.PartIDs[1]) + if err != nil { + return err + } + part02Item, err := s.getPartIDWith(ctx, queries, playerID, data.PartIDs[2]) + if err != nil { + return err + } + part03Item, err := s.getPartIDWith(ctx, queries, playerID, data.PartIDs[3]) + if err != nil { + return err + } + part04Item, err := s.getPartIDWith(ctx, queries, playerID, data.PartIDs[4]) + if err != nil { + return err + } + part05Item, err := s.getPartIDWith(ctx, queries, playerID, data.PartIDs[5]) + if err != nil { + return err + } + part06Item, err := s.getPartIDWith(ctx, queries, playerID, data.PartIDs[6]) + if err != nil { + return err + } + part07Item, err := s.getPartIDWith(ctx, queries, playerID, data.PartIDs[7]) + if err != nil { + return err + } + part08Item, err := s.getPartIDWith(ctx, queries, playerID, data.PartIDs[8]) + if err != nil { + return err + } + part09Item, err := s.getPartIDWith(ctx, queries, playerID, data.PartIDs[9]) + if err != nil { + return err + } + part10Item, err := s.getPartIDWith(ctx, queries, playerID, data.PartIDs[10]) + if err != nil { + return err + } + part11Item, err := s.getPartIDWith(ctx, queries, playerID, data.PartIDs[11]) + if err != nil { + return err + } + part12Item, err := s.getPartIDWith(ctx, queries, playerID, data.PartIDs[12]) + if err != nil { + return err + } + part13Item, err := s.getPartIDWith(ctx, queries, playerID, data.PartIDs[13]) + if err != nil { + return err + } + part14Item, err := s.getPartIDWith(ctx, queries, playerID, data.PartIDs[14]) + if err != nil { + return err + } + part15Item, err := s.getPartIDWith(ctx, queries, playerID, data.PartIDs[15]) + if err != nil { + return err + } + part16Item, err := s.getPartIDWith(ctx, queries, playerID, data.PartIDs[16]) + if err != nil { + return err + } + part17Item, err := s.getPartIDWith(ctx, queries, playerID, data.PartIDs[17]) + if err != nil { + return err + } + part18Item, err := s.getPartIDWith(ctx, queries, playerID, data.PartIDs[18]) + if err != nil { + return err + } + part19Item, err := s.getPartIDWith(ctx, queries, playerID, data.PartIDs[19]) + if err != nil { + return err + } + part20Item, err := s.getPartIDWith(ctx, queries, playerID, data.PartIDs[20]) + if err != nil { + return err + } + part21Item, err := s.getPartIDWith(ctx, queries, playerID, data.PartIDs[21]) + if err != nil { + return err + } + part22Item, err := s.getPartIDWith(ctx, queries, playerID, data.PartIDs[22]) + if err != nil { + return err + } + part23Item, err := s.getPartIDWith(ctx, queries, playerID, data.PartIDs[23]) + if err != nil { + return err + } + cutIn, err := s.getPartIDWith(ctx, queries, playerID, data.CutInID) + if err != nil { + return err + } + + queries.SetCharacterParts(ctx, dbmodels.SetCharacterPartsParams{ + CharacterID: int64(data.ID), + Part00ItemID: part00Item, + Part01ItemID: part01Item, + Part02ItemID: part02Item, + Part03ItemID: part03Item, + Part04ItemID: part04Item, + Part05ItemID: part05Item, + Part06ItemID: part06Item, + Part07ItemID: part07Item, + Part08ItemID: part08Item, + Part09ItemID: part09Item, + Part10ItemID: part10Item, + Part11ItemID: part11Item, + Part12ItemID: part12Item, + Part13ItemID: part13Item, + Part14ItemID: part14Item, + Part15ItemID: part15Item, + Part16ItemID: part16Item, + Part17ItemID: part17Item, + Part18ItemID: part18Item, + Part19ItemID: part19Item, + Part20ItemID: part20Item, + Part21ItemID: part21Item, + Part22ItemID: part22Item, + Part23ItemID: part23Item, + Part00ItemTypeID: int64(data.PartTypeIDs[0]), + Part01ItemTypeID: int64(data.PartTypeIDs[1]), + Part02ItemTypeID: int64(data.PartTypeIDs[2]), + Part03ItemTypeID: int64(data.PartTypeIDs[3]), + Part04ItemTypeID: int64(data.PartTypeIDs[4]), + Part05ItemTypeID: int64(data.PartTypeIDs[5]), + Part06ItemTypeID: int64(data.PartTypeIDs[6]), + Part07ItemTypeID: int64(data.PartTypeIDs[7]), + Part08ItemTypeID: int64(data.PartTypeIDs[8]), + Part09ItemTypeID: int64(data.PartTypeIDs[9]), + Part10ItemTypeID: int64(data.PartTypeIDs[10]), + Part11ItemTypeID: int64(data.PartTypeIDs[11]), + Part12ItemTypeID: int64(data.PartTypeIDs[12]), + Part13ItemTypeID: int64(data.PartTypeIDs[13]), + Part14ItemTypeID: int64(data.PartTypeIDs[14]), + Part15ItemTypeID: int64(data.PartTypeIDs[15]), + Part16ItemTypeID: int64(data.PartTypeIDs[16]), + Part17ItemTypeID: int64(data.PartTypeIDs[17]), + Part18ItemTypeID: int64(data.PartTypeIDs[18]), + Part19ItemTypeID: int64(data.PartTypeIDs[19]), + Part20ItemTypeID: int64(data.PartTypeIDs[20]), + Part21ItemTypeID: int64(data.PartTypeIDs[21]), + Part22ItemTypeID: int64(data.PartTypeIDs[22]), + Part23ItemTypeID: int64(data.PartTypeIDs[23]), + CutInID: cutIn, + }) + + err = tx.Commit() + if err != nil { + return err + } + + return nil + +} + // AddSession adds a new session for a player. func (s *Service) AddSession(ctx context.Context, playerID int64, address string) (dbmodels.Session, error) { sessionKey, err := uuid.NewRandom() if err != nil { return dbmodels.Session{}, err } - return s.database.CreateSession(ctx, dbmodels.CreateSessionParams{ + return s.queries.CreateSession(ctx, dbmodels.CreateSessionParams{ PlayerID: playerID, SessionKey: sessionKey.String(), SessionAddress: address, @@ -147,17 +792,17 @@ func (s *Service) AddSession(ctx context.Context, playerID int64, address string // GetSession gets a session by its ID. func (s *Service) GetSession(ctx context.Context, sessionID int64) (dbmodels.Session, error) { - return s.database.GetSession(ctx, sessionID) + return s.queries.GetSession(ctx, sessionID) } // GetSessionByKey gets a session by its session key. func (s *Service) GetSessionByKey(ctx context.Context, sessionKey string) (dbmodels.Session, error) { - return s.database.GetSessionByKey(ctx, sessionKey) + return s.queries.GetSessionByKey(ctx, sessionKey) } // UpdateSessionExpiry bumps the session expiry value for a session. func (s *Service) UpdateSessionExpiry(ctx context.Context, sessionID int64) (dbmodels.Session, error) { - return s.database.UpdateSessionExpiry(ctx, dbmodels.UpdateSessionExpiryParams{ + return s.queries.UpdateSessionExpiry(ctx, dbmodels.UpdateSessionExpiryParams{ SessionID: sessionID, SessionExpiresAt: time.Now().Add(sessionTimeout).Unix(), }) @@ -165,5 +810,9 @@ func (s *Service) UpdateSessionExpiry(ctx context.Context, sessionID int64) (dbm // DeleteExpiredSessions deletes sessions that have expired. func (s *Service) DeleteExpiredSessions(ctx context.Context) error { - return s.database.DeleteExpiredSessions(ctx, time.Now().Unix()) + return s.queries.DeleteExpiredSessions(ctx, time.Now().Unix()) +} + +func (s *Service) GetPlayerInventory(ctx context.Context, playerID int64) ([]dbmodels.Inventory, error) { + return s.queries.GetPlayerInventory(ctx, playerID) } diff --git a/game/model/room.go b/game/model/room.go index 1bbf0bc..3b75626 100644 --- a/game/model/room.go +++ b/game/model/room.go @@ -53,25 +53,24 @@ type RoomPlayerEntry struct { SlotRankBannerID uint32 StatusFlags RoomStatusFlag Rank uint8 - UnknownPadding [3]byte - Unknown uint8 - Unknown2 uint16 + Unknown uint16 GuildID uint32 GuildEmblemImage string `struct:"[12]byte"` - GuildEmblemID uint8 PlayerID uint32 + Unknown2 uint32 + Unknown3 uint8 // <-- suspect, this may not be in the right place LoungeState uint32 - Unknown3 uint16 - Unknown4 uint32 + Unknown4 uint16 X float32 Y float32 Z float32 Angle float32 + Unknown5 uint32 ShopUnknown uint32 ShopName string `struct:"[64]byte"` MascotTypeID uint32 GlobalID string `struct:"[22]byte"` - Unknown5 [106]byte + Unknown6 [106]byte Guest bool `struct:"byte"` AverageScore float32 CharacterData pangya.PlayerCharacterData diff --git a/game/packet/client.go b/game/packet/client.go index ca9aa11..8847302 100755 --- a/game/packet/client.go +++ b/game/packet/client.go @@ -46,7 +46,7 @@ var ClientMessageTable = common.NewMessageTable(map[uint16]ClientMessage{ 0x0017: &ClientShotItemUse{}, 0x0018: &ClientUserTypingIndicator{}, 0x0019: &ClientShotCometRelief{}, - 0x001A: &Client001A{}, + 0x001A: &ClientHoleInfo{}, 0x001B: &ClientShotSync{}, 0x001C: &ClientRoomSync{}, 0x001D: &ClientBuyItem{}, @@ -71,6 +71,11 @@ var ClientMessageTable = common.NewMessageTable(map[uint16]ClientMessage{ 0x008B: &ClientRequestMessengerList{}, 0x009C: &ClientRequestPlayerHistory{}, 0x00AE: &ClientTutorialClear{}, + 0x00B5: &ClientEnterMyRoom{}, + 0x00B7: &ClientRequestInventory{}, + 0x00C1: &Client00C1{}, + 0x00CC: &ClientLockerCombinationAttempt{}, + 0x00D3: &ClientLockerInventoryRequest{}, 0x00FE: &Client00FE{}, 0x0140: &ClientShopJoin{}, 0x0143: &ClientRequestInboxList{}, @@ -236,8 +241,14 @@ type ClientShotCometRelief struct { X, Y, Z float32 } -type Client001A struct { +type ClientHoleInfo struct { ClientMessage_ + Num uint8 + Unknown1 uint32 + Unknown2 uint32 + Par uint8 + TeeX, TeeZ float32 + PinX, PinZ float32 } type SyncEntry struct { @@ -259,13 +270,13 @@ type ClientRoomSync struct { } type PurchaseItem struct { - Unknown uint32 - ItemID uint32 - Unknown2 uint16 - Unknown3 uint16 - Quantity uint32 - ItemCostPang uint32 - ItemCostCookie uint32 + Unknown uint32 + ItemTypeID uint32 + Unknown2 uint16 + Unknown3 uint16 + Quantity uint32 + ItemCostPang uint32 + ItemCostPoint uint32 } // ClientBuyItem is sent by the client to buy an item from the shop. @@ -314,13 +325,14 @@ type UpdateUnknown2 struct { type ClientEquipmentUpdate struct { ClientMessage_ Type uint8 - Caddie *UpdateCaddie `struct-if:"Type == 1"` - Consumables *UpdateConsumables `struct-if:"Type == 2"` - Comet *UpdateComet `struct-if:"Type == 3"` - Decoration *UpdateDecoration `struct-if:"Type == 4"` - Character *UpdateCharacter `struct-if:"Type == 5"` - Unknown1 *UpdateUnknown1 `struct-if:"Type == 8"` - Unknown2 *UpdateUnknown2 `struct-if:"Type == 9"` + CharParts *pangya.PlayerCharacterData `struct-if:"Type == 0"` + Caddie *UpdateCaddie `struct-if:"Type == 1"` + Consumables *UpdateConsumables `struct-if:"Type == 2"` + Comet *UpdateComet `struct-if:"Type == 3"` + Decoration *UpdateDecoration `struct-if:"Type == 4"` + Character *UpdateCharacter `struct-if:"Type == 5"` + Unknown1 *UpdateUnknown1 `struct-if:"Type == 8"` + Unknown2 *UpdateUnknown2 `struct-if:"Type == 9"` } type ClientShotActiveUserAcknowledge struct { @@ -411,6 +423,18 @@ type ClientTutorialClear struct { ClientMessage_ } +type ClientEnterMyRoom struct { + ClientMessage_ + UserID uint32 + RoomUserID uint32 +} + +type ClientRequestInventory struct { + ClientMessage_ + UserID uint32 + Unknown byte +} + // ClientShopJoin is an unknown message. type ClientShopJoin struct { ClientMessage_ @@ -470,6 +494,20 @@ type ClientRequestDailyReward struct { ClientMessage_ } +type Client00C1 struct { + ClientMessage_ + Unknown byte +} + +type ClientLockerCombinationAttempt struct { + ClientMessage_ + Combination common.PString +} + +type ClientLockerInventoryRequest struct { + ClientMessage_ +} + type Client00FE struct { ClientMessage_ } diff --git a/game/packet/server.go b/game/packet/server.go index a77056f..be9c69c 100755 --- a/game/packet/server.go +++ b/game/packet/server.go @@ -27,7 +27,7 @@ type ServerConn = common.ServerConn[ClientMessage, ServerMessage] var ServerMessageTable = common.NewMessageTable(map[uint16]ServerMessage{ 0x0040: &ServerEvent{}, - 0x0044: &ServerUserData{}, + 0x0044: &ServerPlayerData{}, 0x0046: &ServerUserCensus{}, 0x0047: &ServerRoomList{}, 0x0048: &ServerRoomCensus{}, @@ -50,7 +50,11 @@ var ServerMessageTable = common.NewMessageTable(map[uint16]ServerMessage{ 0x0064: &ServerRoomShotSync{}, 0x0065: &ServerRoomFinishHole{}, 0x0066: &ServerRoomFinishGame{}, + 0x0068: &ServerPurchaseItemResponse{}, + 0x006B: &ServerPlayerEquipmentUpdated{}, 0x0070: &ServerCharData{}, + 0x0072: &ServerPlayerEquipment{}, + 0x0073: &ServerPlayerInventory{}, 0x0076: &ServerGameInit{}, 0x0077: &Server0077{}, 0x0078: &ServerPlayerReady{}, @@ -58,6 +62,7 @@ var ServerMessageTable = common.NewMessageTable(map[uint16]ServerMessage{ 0x0090: &ServerPlayerFirstShotReady{}, 0x0092: &ServerOpponentQuit{}, 0x0095: &ServerMoneyUpdate{}, + 0x0096: &ServerPointsBalance{}, 0x009E: &ServerRoomSetWeather{}, 0x009F: &ServerChannelList{}, 0x00A1: &ServerUserInfo{}, @@ -70,10 +75,16 @@ var ServerMessageTable = common.NewMessageTable(map[uint16]ServerMessage{ 0x00F6: &ServerMultiplayerLeft{}, 0x010E: &ServerPlayerHistory{}, 0x011F: &ServerTutorialStatus{}, + 0x012B: &ServerMyRoomEntered{}, + 0x012D: &ServerMyRoomLayout{}, 0x0151: &Server0151{}, 0x0158: &ServerPlayerStats{}, + 0x0168: &ServerPlayerInfo{}, 0x016A: &Server016A{}, + 0x016C: &ServerLockerCombinationResponse{}, + 0x0170: &ServerLockerInventoryResponse{}, 0x01F6: &Server01F6{}, + 0x020E: &Server020E{}, 0x0210: &ServerInboxNotify{}, 0x0211: &ServerInboxList{}, 0x0212: &ServerMailMessage{}, @@ -142,13 +153,62 @@ type PlayerMainData struct { Unknown2 [321]byte } -// ServerUserData contains important state information. -type ServerUserData struct { +// ServerPlayerData contains important state information. +type ServerPlayerData struct { ServerMessage_ SubType byte MainData *PlayerMainData `struct-if:"SubType == 0"` } +type CaddieUpdated struct { + CaddieID uint32 +} + +type ConsumablesUpdated struct { + ItemTypeID [10]uint32 +} + +type CometUpdated struct { + ItemID uint32 + ItemTypeID uint32 +} + +type DecorationUpdated struct { + BackgroundTypeID uint32 + FrameTypeID uint32 + StickerTypeID uint32 + SlotTypeID uint32 + CutInTypeID uint32 + TitleTypeID uint32 +} + +type CharacterUpdated struct { + CharacterID uint32 +} + +type EquipmentUpdateType uint8 + +const ( + UpdatedCharParts EquipmentUpdateType = 0 + UpdatedCaddie EquipmentUpdateType = 1 + UpdatedConsumables EquipmentUpdateType = 2 + UpdatedComet EquipmentUpdateType = 3 + UpdatedDecoration EquipmentUpdateType = 4 + UpdatedCharacter EquipmentUpdateType = 5 +) + +type ServerPlayerEquipmentUpdated struct { + ServerMessage_ + Status uint8 + Type uint8 + CharParts *pangya.PlayerCharacterData `struct-if:"Type == 0"` + Caddie *CaddieUpdated `struct-if:"Type == 1"` + Consumables *ConsumablesUpdated `struct-if:"Type == 2"` + Comet *CometUpdated `struct-if:"Type == 3"` + Decoration *DecorationUpdated `struct-if:"Type == 4"` + Character *CharacterUpdated `struct-if:"Type == 5"` +} + // ServerCharData contains the user's characters. type ServerCharData struct { ServerMessage_ @@ -157,6 +217,32 @@ type ServerCharData struct { Characters []pangya.PlayerCharacterData } +type ServerPlayerEquipment struct { + ServerMessage_ + Equipment pangya.PlayerEquipment +} + +type InventoryItem struct { + ItemID uint32 + ItemTypeID uint32 + Unknown int32 + Quantity uint32 + Unknown2 [7]byte + Flags byte + RentalDateStart uint32 + Unknown3 uint32 + RentalDateEnd uint32 + Unknown4 uint32 + Unknown5 [156]byte +} + +type ServerPlayerInventory struct { + ServerMessage_ + Remaining uint16 + Count uint16 `struct:"sizeof=Inventory"` + Inventory []InventoryItem +} + type GamePlayer struct { Number uint16 PlayerData pangya.PlayerData @@ -323,6 +409,7 @@ type ServerRoomCensus struct { type RoomCensusListSet struct { PlayerCount uint8 `struct:"sizeof=PlayerList"` PlayerList []gamemodel.RoomPlayerEntry + Unknown byte } type RoomCensusListAdd struct { @@ -484,12 +571,30 @@ type ServerRoomFinishGame struct { Results []PlayerGameResult `struct:"sizefrom=NumPlayers"` } +type ServerPurchaseItemResponse struct { + ServerMessage_ + Status uint32 + Pang uint64 + Points uint64 +} + type Server016A struct { ServerMessage_ Unknown byte Unknown2 uint32 } +type ServerLockerCombinationResponse struct { + ServerMessage_ + Status uint32 +} + +type ServerLockerInventoryResponse struct { + ServerMessage_ + Unknown uint32 + Status uint32 +} + // ServerRoomJoin is sent when a room is joined. type ServerRoomJoin struct { ServerMessage_ @@ -546,6 +651,11 @@ type ServerMoneyUpdate struct { PangBalance *UpdatePangBalanceData `struct-if:"Type == 273"` } +type ServerPointsBalance struct { + ServerMessage_ + Points uint64 +} + type ServerRoomSetWeather struct { ServerMessage_ Weather uint16 @@ -584,6 +694,27 @@ type ServerTutorialStatus struct { Unknown [6]byte } +type ServerMyRoomEntered struct { + ServerMessage_ + Unknown uint32 + UserID uint32 + Unknown2 uint32 + Unknown3 [99]byte +} + +type FurnitureItem struct { + Unknown uint32 + ItemID uint32 + Unknown2 [19]byte +} + +type ServerMyRoomLayout struct { + ServerMessage_ + Unknown uint32 + FurnitureCount uint16 + Furniture []FurnitureItem +} + type Server0151 struct { ServerMessage_ Unknown []byte @@ -597,11 +728,21 @@ type ServerPlayerStats struct { Stats pangya.PlayerStats } +type ServerPlayerInfo struct { + ServerMessage_ + Player gamemodel.RoomPlayerEntry +} + type Server01F6 struct { ServerMessage_ Unknown []byte } +type Server020E struct { + ServerMessage_ + Unknown [8]byte +} + // ServerInboxNotify is unimplemented. type ServerInboxNotify struct { ServerMessage_ diff --git a/game/room/event.go b/game/room/event.go index 6d455f4..fe4c45c 100644 --- a/game/room/event.go +++ b/game/room/event.go @@ -198,6 +198,15 @@ type RoomGameShotSync struct { Data gamemodel.ShotSyncData } +type RoomGameHoleInfo struct { + roomEvent + Par uint8 + TeeX float32 + TeeZ float32 + PinX float32 + PinZ float32 +} + type ChatMessage struct { lobbyEvent roomEvent diff --git a/game/room/room.go b/game/room/room.go index fd76609..3372469 100644 --- a/game/room/room.go +++ b/game/room/room.go @@ -234,6 +234,9 @@ func (r *Room) handleEvent(ctx context.Context, t *actor.Task[RoomEvent], msg ac case RoomGameShotSync: return rejectOnError(r.handleRoomGameShotSync(ctx, event)) + case RoomGameHoleInfo: + return rejectOnError(r.handleRoomGameHoleInfo(ctx, event)) + case ChatMessage: return rejectOnError(r.handleChatMessage(ctx, event)) @@ -570,7 +573,6 @@ func (r *Room) handleRoomGameTurnEnd(ctx context.Context, event RoomGameTurnEnd) if r.state.CurrentHole > r.state.NumHoles { for pair := r.players.Oldest(); pair != nil; pair = pair.Next() { // TODO: - // - This doesn't work (client hangs) // - Need to actually calculate values for real. r.broadcast(ctx, &gamepacket.ServerEvent{ Type: gamepacket.GameEndEvent, @@ -643,6 +645,11 @@ func (r *Room) handleRoomGameShotSync(ctx context.Context, event RoomGameShotSyn return nil } +func (r *Room) handleRoomGameHoleInfo(ctx context.Context, event RoomGameHoleInfo) error { + fmt.Printf("%#v\n", event) + return nil +} + func (r *Room) handleChatMessage(ctx context.Context, event ChatMessage) error { msg := &gamepacket.ServerEvent{Type: gamepacket.ChatMessageEvent} msg.Data.Message = common.ToPString(event.Message) diff --git a/game/server/auth.go b/game/server/auth.go index ad2cf75..3f0f276 100644 --- a/game/server/auth.go +++ b/game/server/auth.go @@ -26,20 +26,63 @@ import ( gamepacket "github.com/pangbox/server/game/packet" "github.com/pangbox/server/gen/proto/go/topologypb" "github.com/pangbox/server/pangya" - log "github.com/sirupsen/logrus" ) func (c *Conn) handleAuth(ctx context.Context) error { - err := c.SendHello(&gamepacket.ConnectMessage{ - Unknown: [8]byte{0x00, 0x06, 0x00, 0x00, 0x3f, 0x00, 0x01, 0x01}, - }) - if err != nil { + if err := c.sendHello(ctx); err != nil { return fmt.Errorf("sending hello message: %w", err) } + if err := c.waitForSessionAuth(ctx); err != nil { + return fmt.Errorf("waiting for session auth: %w", err) + } + + if err := c.fetchPlayer(ctx); err != nil { + return fmt.Errorf("fetching player from db: %w", err) + } + + if err := c.fetchCharacters(ctx); err != nil { + return fmt.Errorf("fetching characters from db: %w", err) + } + + if err := c.sendPlayerData(ctx); err != nil { + return fmt.Errorf("sending player data to client: %w", err) + } + + if err := c.sendCharacterData(ctx); err != nil { + return fmt.Errorf("sending character data to client: %w", err) + } + + if err := c.sendAchievementProgress(ctx); err != nil { + return fmt.Errorf("sending achievemtn progress to client: %w", err) + } + + if err := c.sendInventory(ctx); err != nil { + return fmt.Errorf("sending inventory to client: %w", err) + } + + if err := c.SendMessage(ctx, &gamepacket.ServerMessageConnect{}); err != nil { + return fmt.Errorf("sending message server connect message: %w", err) + } + + if err := c.sendServerList(ctx); err != nil { + return fmt.Errorf("sending server list: %w", err) + } + + return nil +} + +func (c *Conn) sendHello(ctx context.Context) error { + // TODO: remove hardcoded bytes + return c.SendHello(&gamepacket.ConnectMessage{ + Unknown: [8]byte{0x00, 0x06, 0x00, 0x00, 0x3f, 0x00, 0x01, 0x01}, + }) +} + +func (c *Conn) waitForSessionAuth(ctx context.Context) error { msg, err := c.ReadMessage() if err != nil { - return fmt.Errorf("reading handshake: %w", err) + return fmt.Errorf("reading message: %w", err) } switch t := msg.(type) { @@ -47,83 +90,119 @@ func (c *Conn) handleAuth(ctx context.Context) error { c.session, err = c.s.accountsService.GetSessionByKey(ctx, t.LoginKey.Value) if err != nil { // TODO: error handling - return nil + return err } - c.player, err = c.s.accountsService.GetPlayer(ctx, c.session.PlayerID) - if err != nil { - // TODO: error handling - return nil - } - log.Debugf("Client auth: %#v", msg) + c.connID = uint32(c.session.SessionID) default: return fmt.Errorf("expected client auth, got %T", t) } + return nil +} + +func (c *Conn) fetchPlayer(ctx context.Context) error { + var err error + c.player, err = c.s.accountsService.GetPlayer(ctx, c.session.PlayerID) + if err != nil { + // TODO: error handling + return err + } + return nil +} + +func (c *Conn) refreshCurrentCharacter() { + for _, character := range c.characters { + if character.ID == uint32(c.player.CharacterID.Int64) { + c.currentCharacter = &character + return + } + } + if len(c.characters) > 0 { + c.currentCharacter = &c.characters[0] + return + } else { + c.currentCharacter = nil + } +} +func (c *Conn) fetchCharacters(ctx context.Context) error { + var err error c.characters, err = c.s.accountsService.GetCharacters(ctx, c.session.PlayerID) if err != nil { // TODO: handle error for client return fmt.Errorf("database error: %w", err) } + c.refreshCurrentCharacter() - c.connID = uint32(c.session.SessionID) - - // TODO: need data modelling - c.playerData = pangya.PlayerData{ - UserInfo: pangya.PlayerInfo{ - Username: c.player.Username, - Nickname: c.player.Nickname.String, - PlayerID: uint32(c.player.PlayerID), - ConnID: c.connID, - }, - PlayerStats: pangya.PlayerStats{ - Pang: uint64(c.player.Pang), - }, - Items: pangya.PlayerEquipment{ - CaddieID: 0, - CharacterID: c.characters[0].ID, - ClubSetID: 0x1754, - CometTypeID: 0x14000000, - Items: pangya.PlayerEquippedItems{ - ItemIDs: [10]uint32{0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - }, - }, - EquippedCharacter: c.characters[0], - EquippedClub: pangya.PlayerClubData{ - Item: pangya.PlayerItem{ - ID: 0x1754, - TypeID: 0x10000000, - }, - Stats: pangya.ClubStats{ - UpgradeStats: [5]uint16{8, 9, 8, 3, 3}, - }, - }, - } + return nil +} - c.SendMessage(ctx, &gamepacket.ServerUserData{ +func (c *Conn) sendPlayerData(ctx context.Context) error { + return c.SendMessage(ctx, &gamepacket.ServerPlayerData{ SubType: 0, MainData: &gamepacket.PlayerMainData{ ClientVersion: common.ToPString("824.00"), ServerVersion: common.ToPString("Pangbox"), Game: 0xFFFF, - PlayerData: c.playerData, + PlayerData: c.getPlayerData(), }, }) +} - c.SendMessage(ctx, &gamepacket.ServerCharData{ +func (c *Conn) sendCharacterData(ctx context.Context) error { + return c.SendMessage(ctx, &gamepacket.ServerCharData{ Count1: uint16(len(c.characters)), Count2: uint16(len(c.characters)), Characters: c.characters, }) +} - c.SendMessage(ctx, &gamepacket.ServerAchievementProgress{ +func (c *Conn) sendAchievementProgress(ctx context.Context) error { + return c.SendMessage(ctx, &gamepacket.ServerAchievementProgress{ Remaining: 0, Count: 0, }) +} - c.SendMessage(ctx, &gamepacket.ServerMessageConnect{}) +func (c *Conn) sendInventory(ctx context.Context) error { + inventory, err := c.s.accountsService.GetPlayerInventory(ctx, c.session.PlayerID) + if err != nil { + return fmt.Errorf("database error: %w", err) + } - c.sendServerList(ctx) + for i, l := 0, 50; i < len(inventory); { + if l > len(inventory) { + l = len(inventory) + } + inventoryPkt := &gamepacket.ServerPlayerInventory{ + Remaining: uint16((len(inventory) - i) - l), + Count: uint16(l), + Inventory: make([]gamepacket.InventoryItem, l), + } + for n := i + l; i < n; i++ { + inventoryPkt.Inventory[i] = gamepacket.InventoryItem{ + ItemID: uint32(inventory[i].ItemID), + ItemTypeID: uint32(inventory[i].ItemTypeID), + Unknown: -1, + Quantity: uint32(inventory[i].Quantity.Int64), + Unknown5: [156]byte{ + 0x02, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }, + } + } + if err := c.SendMessage(ctx, inventoryPkt); err != nil { + return err + } + } return nil } diff --git a/game/server/conn.go b/game/server/conn.go index 2e020c9..907dbab 100755 --- a/game/server/conn.go +++ b/game/server/conn.go @@ -19,9 +19,11 @@ package gameserver import ( "context" + "errors" "fmt" "github.com/pangbox/server/common" + "github.com/pangbox/server/database/accounts" gamemodel "github.com/pangbox/server/game/model" gamepacket "github.com/pangbox/server/game/packet" "github.com/pangbox/server/game/room" @@ -36,10 +38,11 @@ type Conn struct { connID uint32 session dbmodels.Session - player dbmodels.Player - playerData pangya.PlayerData + player dbmodels.GetPlayerRow characters []pangya.PlayerCharacterData + currentCharacter *pangya.PlayerCharacterData + currentLobby *room.Lobby currentRoom *room.Room } @@ -62,11 +65,11 @@ func (c *Conn) getRoomPlayer() *gamemodel.RoomPlayerEntry { Nickname: c.player.Nickname.String, Rank: uint8(c.player.Rank), GuildName: "", - CharTypeID: c.playerData.EquippedCharacter.CharTypeID, + CharTypeID: c.currentCharacter.CharTypeID, StatusFlags: 0, GuildEmblemImage: "guildmark", // TODO PlayerID: uint32(c.player.PlayerID), - CharacterData: c.playerData.EquippedCharacter, + CharacterData: c.getPlayerEquippedCharacter(), } } @@ -179,7 +182,7 @@ func (c *Conn) Handle(ctx context.Context) error { c.currentRoom.Send(ctx, room.RoomPlayerJoin{ Entry: c.getRoomPlayer(), Conn: c.ServerConn, - PlayerData: c.playerData, + PlayerData: c.getPlayerData(), }) case *gamepacket.ClientAssistModeToggle: c.SendMessage(ctx, &gamepacket.ServerAssistModeToggled{}) @@ -317,8 +320,6 @@ func (c *Conn) Handle(ctx context.Context) error { Message: common.ToPString("Welcome to the first Pangbox server release! Not much works yet..."), }, }) - case *gamepacket.Client001A: - // Do nothing. case *gamepacket.ClientJoinChannel: c.SendMessage(ctx, &gamepacket.Server004E{Unknown: []byte{0x01}}) c.SendMessage(ctx, &gamepacket.Server01F6{Unknown: []byte{0x00, 0x00, 0x00, 0x00}}) @@ -362,10 +363,21 @@ func (c *Conn) Handle(ctx context.Context) error { joinRoom.Send(ctx, room.RoomPlayerJoin{ Entry: c.getRoomPlayer(), Conn: c.ServerConn, - PlayerData: c.playerData, + PlayerData: c.getPlayerData(), }) c.currentRoom = joinRoom } + case *gamepacket.ClientHoleInfo: + if c.currentRoom == nil { + break + } + c.currentRoom.Send(ctx, room.RoomGameHoleInfo{ + Par: t.Par, + TeeX: t.TeeX, + TeeZ: t.TeeZ, + PinX: t.PinX, + PinZ: t.PinZ, + }) case *gamepacket.ClientRoomLeave: if err := c.leaveRoom(ctx); err != nil { // TODO: handle error @@ -436,18 +448,202 @@ func (c *Conn) Handle(ctx context.Context) error { c.SendMessage(ctx, &gamepacket.ServerTutorialStatus{ Unknown: [6]byte{0x00, 0x01, 0x03, 0x00, 0x00, 0x00}, }) + case *gamepacket.ClientEnterMyRoom: + if t.UserID != t.RoomUserID { + return errors.New("entering another user's myroom is not implemented yet") + } + c.SendMessage(ctx, &gamepacket.ServerMyRoomEntered{ + Unknown: 1, + UserID: t.UserID, + Unknown2: 1, + }) + case *gamepacket.ClientRequestInventory: + if t.UserID != uint32(c.session.PlayerID) { + return errors.New("wrong user ID specified in packet") + } + c.SendMessage(ctx, &gamepacket.ServerMyRoomLayout{ + Unknown: 1, + FurnitureCount: 0, + }) + c.SendMessage(ctx, &gamepacket.ServerPlayerInfo{ + Player: *c.getRoomPlayer(), + }) + case *gamepacket.ClientLockerCombinationAttempt: + // TODO + c.SendMessage(ctx, &gamepacket.ServerLockerCombinationResponse{ + Status: 0, + }) + case *gamepacket.ClientLockerInventoryRequest: + // This has something to do with the combination/password system. + c.SendMessage(ctx, &gamepacket.ServerLockerInventoryResponse{ + Status: 76, + }) + case *gamepacket.Client00C1: + // Seems to happen when entering my room. + log.Debug("TODO: 00C1 (my room?)") case *gamepacket.ClientUserMacrosSet: // TODO: server-side macro storage log.Debugf("Set macros: %+v", t.MacroList) case *gamepacket.ClientEquipmentUpdate: - // TODO - log.Debug("TODO: 0020") + switch { + case t.CharParts != nil: + if err := c.s.accountsService.SetCharacterParts( + ctx, + c.player.PlayerID, + *t.CharParts, + ); err != nil { + return err + } + if err := c.fetchPlayer(ctx); err != nil { + return err + } + if err := c.SendMessage(ctx, &gamepacket.ServerPlayerEquipmentUpdated{ + Status: 0x04, + Type: uint8(gamepacket.UpdatedCharParts), + CharParts: c.currentCharacter, + }); err != nil { + return err + } + + case t.Caddie != nil: + if err := c.s.accountsService.SetCaddie(ctx, c.player.PlayerID, int64(t.Caddie.CaddieID)); err != nil { + return err + } + if err := c.fetchPlayer(ctx); err != nil { + return err + } + if err := c.SendMessage(ctx, &gamepacket.ServerPlayerEquipmentUpdated{ + Status: 0x04, + Type: uint8(gamepacket.UpdatedCaddie), + Caddie: &gamepacket.CaddieUpdated{ + CaddieID: t.Caddie.CaddieID, + }, + }); err != nil { + return err + } + + case t.Consumables != nil: + if err := c.s.accountsService.SetConsumables(ctx, c.player.PlayerID, t.Consumables.ItemTypeID, &c.player); err != nil { + return err + } + if err := c.SendMessage(ctx, &gamepacket.ServerPlayerEquipmentUpdated{ + Status: 0x04, + Type: uint8(gamepacket.UpdatedConsumables), + Consumables: &gamepacket.ConsumablesUpdated{ + ItemTypeID: c.getPlayerEquippedConsumables(), + }, + }); err != nil { + return err + } + + case t.Comet != nil: + cometID, err := c.s.accountsService.SetComet(ctx, c.player.PlayerID, t.Comet.ItemTypeID, &c.player) + if err != nil { + log.WithError(err).Errorf("attempt to set comet failed") + } + if err := c.SendMessage(ctx, &gamepacket.ServerPlayerEquipmentUpdated{ + Status: 0x04, + Type: uint8(gamepacket.UpdatedComet), + Comet: &gamepacket.CometUpdated{ + ItemID: uint32(cometID), + ItemTypeID: uint32(c.player.BallTypeID), + }, + }); err != nil { + return err + } + + case t.Decoration != nil: + if err := c.s.accountsService.SetDecoration(ctx, c.player.PlayerID, accounts.DecorationTypeIDs{ + BackgroundTypeID: t.Decoration.BackgroundTypeID, + FrameTypeID: t.Decoration.FrameTypeID, + StickerTypeID: t.Decoration.StickerTypeID, + SlotTypeID: t.Decoration.SlotTypeID, + CutInTypeID: t.Decoration.CutInTypeID, + TitleTypeID: t.Decoration.TitleTypeID, + }); err != nil { + return err + } + if err := c.fetchPlayer(ctx); err != nil { + return err + } + if err := c.SendMessage(ctx, &gamepacket.ServerPlayerEquipmentUpdated{ + Status: 0x04, + Type: uint8(gamepacket.UpdatedDecoration), + Decoration: &gamepacket.DecorationUpdated{ + BackgroundTypeID: uint32(c.player.BackgroundTypeID.Int64), + FrameTypeID: uint32(c.player.FrameTypeID.Int64), + StickerTypeID: uint32(c.player.StickerTypeID.Int64), + SlotTypeID: uint32(c.player.SlotTypeID.Int64), + CutInTypeID: uint32(c.player.CutInTypeID.Int64), + TitleTypeID: uint32(c.player.TitleTypeID.Int64), + }, + }); err != nil { + return err + } + + case t.Character != nil: + if err := c.s.accountsService.SetCharacter(ctx, c.player.PlayerID, int64(t.Character.CharacterID)); err != nil { + return err + } + if err := c.fetchPlayer(ctx); err != nil { + return err + } + c.refreshCurrentCharacter() + if err := c.SendMessage(ctx, &gamepacket.ServerPlayerEquipmentUpdated{ + Status: 0x04, + Type: uint8(gamepacket.UpdatedCharParts), + CharParts: c.currentCharacter, + }); err != nil { + return err + } + } case *gamepacket.Client00FE: // TODO log.Debug("TODO: 00FE") case *gamepacket.ClientShopJoin: - // Enter shop, not sure what responses need to go here? - log.Debug("TODO: 0140") + c.SendMessage(ctx, &gamepacket.Server020E{}) + case *gamepacket.ClientBuyItem: + pangTotal := int64(0) + pointTotal := int64(0) + for _, item := range t.Items { + // TODO: validate with iff + pangTotal += int64(item.ItemCostPang) + pointTotal += int64(item.ItemCostPoint) + } + if pangTotal > c.player.Pang || pointTotal > c.player.Points { + c.SendMessage(ctx, &gamepacket.ServerPurchaseItemResponse{ + Status: 1, + }) + continue + } + newCurrency := dbmodels.SetPlayerCurrencyRow{} + for _, item := range t.Items { + newCurrency, err = c.s.accountsService.PurchaseItem(ctx, c.session.PlayerID, int64(item.ItemCostPang), int64(item.ItemCostPoint), int64(item.ItemTypeID), int64(item.Quantity)) + if err != nil { + return fmt.Errorf("purchasing item %v: %w", item.ItemTypeID, err) + } + } + if err := c.SendMessage(ctx, &gamepacket.ServerPangPurchaseData{ + PangsRemaining: uint64(newCurrency.Pang), + PangsSpent: uint64(pangTotal), + }); err != nil { + return err + } + if err := c.SendMessage(ctx, &gamepacket.ServerPointsBalance{ + Points: uint64(newCurrency.Points), + }); err != nil { + return err + } + if err := c.SendMessage(ctx, &gamepacket.ServerPurchaseItemResponse{ + Status: 0, + Pang: uint64(newCurrency.Pang), + Points: uint64(newCurrency.Points), + }); err != nil { + return err + } + c.sendInventory(ctx) + c.fetchCharacters(ctx) + c.sendCharacterData(ctx) default: return fmt.Errorf("unexpected message: %T", t) } diff --git a/game/server/playerdata.go b/game/server/playerdata.go new file mode 100644 index 0000000..4ac9481 --- /dev/null +++ b/game/server/playerdata.go @@ -0,0 +1,112 @@ +// Copyright (C) 2018-2023, John Chadwick +// +// Permission to use, copy, modify, and/or distribute this software for any purpose +// with or without fee is hereby granted, provided that the above copyright notice +// and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +// FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +// OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +// THIS SOFTWARE. +// +// SPDX-FileCopyrightText: Copyright (c) 2018-2023 John Chadwick +// SPDX-License-Identifier: ISC + +package gameserver + +import "github.com/pangbox/server/pangya" + +func (c *Conn) getPlayerInfo() pangya.PlayerInfo { + return pangya.PlayerInfo{ + Username: c.player.Username, + Nickname: c.player.Nickname.String, + PlayerID: uint32(c.player.PlayerID), + ConnID: c.connID, + // TODO + } +} + +func (c *Conn) getPlayerStats() pangya.PlayerStats { + return pangya.PlayerStats{ + Pang: uint64(c.player.Pang), + // TODO + } +} + +func (c *Conn) getPlayerEquippedConsumables() [10]uint32 { + return [10]uint32{ + uint32(c.player.Slot0TypeID), + uint32(c.player.Slot1TypeID), + uint32(c.player.Slot2TypeID), + uint32(c.player.Slot3TypeID), + uint32(c.player.Slot4TypeID), + uint32(c.player.Slot5TypeID), + uint32(c.player.Slot6TypeID), + uint32(c.player.Slot7TypeID), + uint32(c.player.Slot8TypeID), + uint32(c.player.Slot9TypeID), + } +} + +func (c *Conn) getPlayerEquippedItems() pangya.PlayerEquipment { + comet := c.player.BallTypeID + if comet == 0 { + comet = 0x14000000 + } + return pangya.PlayerEquipment{ + CaddieID: uint32(c.player.CaddieID.Int64), + CharacterID: uint32(c.player.CharacterID.Int64), + ClubSetID: uint32(c.player.ClubID.Int64), + CometTypeID: uint32(comet), + Items: pangya.PlayerEquippedItems{ + ItemIDs: c.getPlayerEquippedConsumables(), + }, + BackgroundID: uint32(c.player.BackgroundID.Int64), + FrameID: uint32(c.player.FrameID.Int64), + StickerID: uint32(c.player.StickerID.Int64), + SlotID: uint32(c.player.SlotID.Int64), + CutInID: uint32(c.player.CutInID.Int64), + TitleID: uint32(c.player.TitleID.Int64), + BackgroundTypeID: uint32(c.player.BackgroundTypeID.Int64), + FrameTypeID: uint32(c.player.FrameTypeID.Int64), + StickerTypeID: uint32(c.player.StickerTypeID.Int64), + SlotTypeID: uint32(c.player.SlotTypeID.Int64), + CutInTypeID: uint32(c.player.CutInTypeID.Int64), + TitleTypeID: uint32(c.player.TitleTypeID.Int64), + MascotID: uint32(c.player.MascotTypeID), + PosterID: [2]uint32{ + uint32(c.player.Poster0TypeID.Int64), + uint32(c.player.Poster1TypeID.Int64), + }, + } +} + +func (c *Conn) getPlayerEquippedCharacter() pangya.PlayerCharacterData { + return *c.currentCharacter +} + +func (c *Conn) getPlayerEquippedClubSet() pangya.PlayerClubData { + return pangya.PlayerClubData{ + Item: pangya.PlayerItem{ + ID: uint32(c.player.ClubID.Int64), + TypeID: uint32(c.player.ClubTypeID.Int64), + }, + // TODO: stats/enchantments are not implemented. + Stats: pangya.ClubStats{ + UpgradeStats: [5]uint16{8, 9, 8, 3, 3}, + }, + } +} + +func (c *Conn) getPlayerData() pangya.PlayerData { + return pangya.PlayerData{ + UserInfo: c.getPlayerInfo(), + PlayerStats: c.getPlayerStats(), + EquippedItems: c.getPlayerEquippedItems(), + EquippedCharacter: c.getPlayerEquippedCharacter(), + EquippedClub: c.getPlayerEquippedClubSet(), + } +} diff --git a/game/server/server.go b/game/server/server.go index 11712e3..dbcdefc 100755 --- a/game/server/server.go +++ b/game/server/server.go @@ -25,6 +25,7 @@ import ( "github.com/pangbox/server/database/accounts" gamepacket "github.com/pangbox/server/game/packet" "github.com/pangbox/server/game/room" + "github.com/pangbox/server/gameconfig" "github.com/pangbox/server/gen/proto/go/topologypb/topologypbconnect" "github.com/pangbox/server/pangya/iff" log "github.com/sirupsen/logrus" @@ -37,6 +38,7 @@ type Options struct { PangyaIFF *iff.Archive ServerID uint32 ChannelName string + ConfigProvider gameconfig.Provider } // Server provides an implementation of the PangYa game server. @@ -47,6 +49,7 @@ type Server struct { pangyaIFF *iff.Archive serverID uint32 channelName string + configProvider gameconfig.Provider lobby *room.Lobby logger *log.Entry } @@ -61,6 +64,7 @@ func New(opts Options) *Server { pangyaIFF: opts.PangyaIFF, serverID: opts.ServerID, channelName: opts.ChannelName, + configProvider: opts.ConfigProvider, logger: logger, } } diff --git a/gameconfig/config.go b/gameconfig/config.go new file mode 100644 index 0000000..9875951 --- /dev/null +++ b/gameconfig/config.go @@ -0,0 +1,90 @@ +package gameconfig + +import ( + "bytes" + _ "embed" + "encoding/json" + "errors" + "io" + "log" + "os" +) + +//go:embed default.json +var defaultJSON []byte +var defaultProvider Provider + +func init() { + var err error + defaultProvider, err = FromJSONStream(bytes.NewReader(defaultJSON)) + if err != nil { + log.Fatalf("Error loading default gameconfig: %v - please report this.", err) + } +} + +type Provider interface { + GetCharacterDefaults(id uint8) CharacterDefaults + GetDefaultClubSetTypeID() uint32 +} + +type CharacterDefaults struct { + CharacterID uint8 `json:"CharacterID"` + DefaultPartTypeIDs [24]uint32 `json:"DefaultPartTypeIDs"` +} + +type Manifest struct { + CharacterDefaults []CharacterDefaults `json:"CharacterDefaults"` + DefaultClubSetTypeID uint32 `json:"DefaultClubSetTypeID"` +} + +type configFileProvider struct { + characterDefaults map[uint8]CharacterDefaults + defaultClubSetTypeID uint32 +} + +func Default() Provider { + return defaultProvider +} + +func FromJSONFile(filename string) (Provider, error) { + file, err := os.Open(filename) + if err != nil { + return nil, err + } + provider, err := FromJSONStream(file) + err = errors.Join(err, file.Close()) + if err != nil { + return nil, err + } + return provider, nil +} + +func FromJSONStream(r io.Reader) (Provider, error) { + manifest := Manifest{} + dec := json.NewDecoder(r) + dec.DisallowUnknownFields() + err := dec.Decode(&manifest) + if err != nil { + return nil, err + } + return FromManifest(manifest), nil +} + +func FromManifest(manifest Manifest) Provider { + provider := &configFileProvider{ + characterDefaults: make(map[uint8]CharacterDefaults), + defaultClubSetTypeID: manifest.DefaultClubSetTypeID, + } + for _, defaults := range manifest.CharacterDefaults { + provider.characterDefaults[defaults.CharacterID] = defaults + } + return provider +} + +func (c *configFileProvider) GetCharacterDefaults(id uint8) CharacterDefaults { + return c.characterDefaults[id] +} + +func (c *configFileProvider) GetDefaultClubSetTypeID() uint32 { + return c.defaultClubSetTypeID +} diff --git a/gameconfig/default.json b/gameconfig/default.json new file mode 100644 index 0000000..b8009cb --- /dev/null +++ b/gameconfig/default.json @@ -0,0 +1,4 @@ +{ + "DefaultClubSetTypeID": 268435553, + "CharacterDefaults": [] +} diff --git a/gen.go b/gen.go index dcd513b..753963d 100755 --- a/gen.go +++ b/gen.go @@ -17,6 +17,6 @@ package server +//go:generate ./sqlc.sh //go:generate go run github.com/bufbuild/buf/cmd/buf generate -//go:generate go run github.com/kyleconroy/sqlc/cmd/sqlc generate --no-remote -//go:generate go run github.com/pangbox/server/cmd/minibox/lang/update -src cmd/minibox -out cmd/minibox/lang -locales en,jp +//go:generate go run github.com/pangbox/server/cmd/minibox/lang/update -src cmd/minibox -out cmd/minibox/lang -locales en,ja diff --git a/gen/dbmodels/character.sql.go b/gen/dbmodels/character.sql.go index f9f455c..907d3a5 100644 --- a/gen/dbmodels/character.sql.go +++ b/gen/dbmodels/character.sql.go @@ -7,73 +7,510 @@ package dbmodels import ( "context" + "database/sql" ) const createCharacter = `-- name: CreateCharacter :one INSERT INTO character ( player_id, - character_type_id, - character_data + item_id, + hair_color, + shirt, + mastery, + part00_item_type_id, + part01_item_type_id, + part02_item_type_id, + part03_item_type_id, + part04_item_type_id, + part05_item_type_id, + part06_item_type_id, + part07_item_type_id, + part08_item_type_id, + part09_item_type_id, + part10_item_type_id, + part11_item_type_id, + part12_item_type_id, + part13_item_type_id, + part14_item_type_id, + part15_item_type_id, + part16_item_type_id, + part17_item_type_id, + part18_item_type_id, + part19_item_type_id, + part20_item_type_id, + part21_item_type_id, + part22_item_type_id, + part23_item_type_id ) VALUES ( - ?, ?, ? + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ? ) -RETURNING character_id, player_id, character_type_id, character_data +RETURNING character_id, player_id, item_id, hair_color, shirt, mastery, part00_item_id, part01_item_id, part02_item_id, part03_item_id, part04_item_id, part05_item_id, part06_item_id, part07_item_id, part08_item_id, part09_item_id, part10_item_id, part11_item_id, part12_item_id, part13_item_id, part14_item_id, part15_item_id, part16_item_id, part17_item_id, part18_item_id, part19_item_id, part20_item_id, part21_item_id, part22_item_id, part23_item_id, part00_item_type_id, part01_item_type_id, part02_item_type_id, part03_item_type_id, part04_item_type_id, part05_item_type_id, part06_item_type_id, part07_item_type_id, part08_item_type_id, part09_item_type_id, part10_item_type_id, part11_item_type_id, part12_item_type_id, part13_item_type_id, part14_item_type_id, part15_item_type_id, part16_item_type_id, part17_item_type_id, part18_item_type_id, part19_item_type_id, part20_item_type_id, part21_item_type_id, part22_item_type_id, part23_item_type_id, aux_part0_id, aux_part1_id, aux_part2_id, aux_part3_id, aux_part4_id, cut_in_id ` type CreateCharacterParams struct { - PlayerID int64 - CharacterTypeID int64 - CharacterData []byte + PlayerID int64 + ItemID int64 + HairColor int64 + Shirt int64 + Mastery int64 + Part00ItemTypeID int64 + Part01ItemTypeID int64 + Part02ItemTypeID int64 + Part03ItemTypeID int64 + Part04ItemTypeID int64 + Part05ItemTypeID int64 + Part06ItemTypeID int64 + Part07ItemTypeID int64 + Part08ItemTypeID int64 + Part09ItemTypeID int64 + Part10ItemTypeID int64 + Part11ItemTypeID int64 + Part12ItemTypeID int64 + Part13ItemTypeID int64 + Part14ItemTypeID int64 + Part15ItemTypeID int64 + Part16ItemTypeID int64 + Part17ItemTypeID int64 + Part18ItemTypeID int64 + Part19ItemTypeID int64 + Part20ItemTypeID int64 + Part21ItemTypeID int64 + Part22ItemTypeID int64 + Part23ItemTypeID int64 } func (q *Queries) CreateCharacter(ctx context.Context, arg CreateCharacterParams) (Character, error) { - row := q.db.QueryRowContext(ctx, createCharacter, arg.PlayerID, arg.CharacterTypeID, arg.CharacterData) + row := q.db.QueryRowContext(ctx, createCharacter, + arg.PlayerID, + arg.ItemID, + arg.HairColor, + arg.Shirt, + arg.Mastery, + arg.Part00ItemTypeID, + arg.Part01ItemTypeID, + arg.Part02ItemTypeID, + arg.Part03ItemTypeID, + arg.Part04ItemTypeID, + arg.Part05ItemTypeID, + arg.Part06ItemTypeID, + arg.Part07ItemTypeID, + arg.Part08ItemTypeID, + arg.Part09ItemTypeID, + arg.Part10ItemTypeID, + arg.Part11ItemTypeID, + arg.Part12ItemTypeID, + arg.Part13ItemTypeID, + arg.Part14ItemTypeID, + arg.Part15ItemTypeID, + arg.Part16ItemTypeID, + arg.Part17ItemTypeID, + arg.Part18ItemTypeID, + arg.Part19ItemTypeID, + arg.Part20ItemTypeID, + arg.Part21ItemTypeID, + arg.Part22ItemTypeID, + arg.Part23ItemTypeID, + ) var i Character err := row.Scan( &i.CharacterID, &i.PlayerID, - &i.CharacterTypeID, - &i.CharacterData, + &i.ItemID, + &i.HairColor, + &i.Shirt, + &i.Mastery, + &i.Part00ItemID, + &i.Part01ItemID, + &i.Part02ItemID, + &i.Part03ItemID, + &i.Part04ItemID, + &i.Part05ItemID, + &i.Part06ItemID, + &i.Part07ItemID, + &i.Part08ItemID, + &i.Part09ItemID, + &i.Part10ItemID, + &i.Part11ItemID, + &i.Part12ItemID, + &i.Part13ItemID, + &i.Part14ItemID, + &i.Part15ItemID, + &i.Part16ItemID, + &i.Part17ItemID, + &i.Part18ItemID, + &i.Part19ItemID, + &i.Part20ItemID, + &i.Part21ItemID, + &i.Part22ItemID, + &i.Part23ItemID, + &i.Part00ItemTypeID, + &i.Part01ItemTypeID, + &i.Part02ItemTypeID, + &i.Part03ItemTypeID, + &i.Part04ItemTypeID, + &i.Part05ItemTypeID, + &i.Part06ItemTypeID, + &i.Part07ItemTypeID, + &i.Part08ItemTypeID, + &i.Part09ItemTypeID, + &i.Part10ItemTypeID, + &i.Part11ItemTypeID, + &i.Part12ItemTypeID, + &i.Part13ItemTypeID, + &i.Part14ItemTypeID, + &i.Part15ItemTypeID, + &i.Part16ItemTypeID, + &i.Part17ItemTypeID, + &i.Part18ItemTypeID, + &i.Part19ItemTypeID, + &i.Part20ItemTypeID, + &i.Part21ItemTypeID, + &i.Part22ItemTypeID, + &i.Part23ItemTypeID, + &i.AuxPart0ID, + &i.AuxPart1ID, + &i.AuxPart2ID, + &i.AuxPart3ID, + &i.AuxPart4ID, + &i.CutInID, ) return i, err } const getCharacter = `-- name: GetCharacter :one -SELECT character_id, player_id, character_type_id, character_data FROM character -WHERE character_id = ? LIMIT 1 +SELECT + character.character_id, character.player_id, character.item_id, character.hair_color, character.shirt, character.mastery, character.part00_item_id, character.part01_item_id, character.part02_item_id, character.part03_item_id, character.part04_item_id, character.part05_item_id, character.part06_item_id, character.part07_item_id, character.part08_item_id, character.part09_item_id, character.part10_item_id, character.part11_item_id, character.part12_item_id, character.part13_item_id, character.part14_item_id, character.part15_item_id, character.part16_item_id, character.part17_item_id, character.part18_item_id, character.part19_item_id, character.part20_item_id, character.part21_item_id, character.part22_item_id, character.part23_item_id, character.part00_item_type_id, character.part01_item_type_id, character.part02_item_type_id, character.part03_item_type_id, character.part04_item_type_id, character.part05_item_type_id, character.part06_item_type_id, character.part07_item_type_id, character.part08_item_type_id, character.part09_item_type_id, character.part10_item_type_id, character.part11_item_type_id, character.part12_item_type_id, character.part13_item_type_id, character.part14_item_type_id, character.part15_item_type_id, character.part16_item_type_id, character.part17_item_type_id, character.part18_item_type_id, character.part19_item_type_id, character.part20_item_type_id, character.part21_item_type_id, character.part22_item_type_id, character.part23_item_type_id, character.aux_part0_id, character.aux_part1_id, character.aux_part2_id, character.aux_part3_id, character.aux_part4_id, character.cut_in_id, + inventory_character.item_type_id AS character_type_id, + inventory_aux_part0.item_type_id AS inventory_aux_part0_type_id_FIXNULL, + inventory_aux_part1.item_type_id AS inventory_aux_part1_type_id_FIXNULL, + inventory_aux_part2.item_type_id AS inventory_aux_part2_type_id_FIXNULL, + inventory_aux_part3.item_type_id AS inventory_aux_part3_type_id_FIXNULL, + inventory_aux_part4.item_type_id AS inventory_aux_part4_type_id_FIXNULL +FROM character AS character +LEFT JOIN inventory AS inventory_character ON (character.item_id = inventory_character.item_id) +LEFT JOIN inventory AS inventory_aux_part0 ON (character.aux_part0_id = inventory_aux_part0.item_id) +LEFT JOIN inventory AS inventory_aux_part1 ON (character.aux_part1_id = inventory_aux_part1.item_id) +LEFT JOIN inventory AS inventory_aux_part2 ON (character.aux_part2_id = inventory_aux_part2.item_id) +LEFT JOIN inventory AS inventory_aux_part3 ON (character.aux_part3_id = inventory_aux_part3.item_id) +LEFT JOIN inventory AS inventory_aux_part4 ON (character.aux_part4_id = inventory_aux_part4.item_id) +WHERE character.character_id = ? LIMIT 1 ` -func (q *Queries) GetCharacter(ctx context.Context, characterID int64) (Character, error) { +type GetCharacterRow struct { + CharacterID int64 + PlayerID int64 + ItemID int64 + HairColor int64 + Shirt int64 + Mastery int64 + Part00ItemID sql.NullInt64 + Part01ItemID sql.NullInt64 + Part02ItemID sql.NullInt64 + Part03ItemID sql.NullInt64 + Part04ItemID sql.NullInt64 + Part05ItemID sql.NullInt64 + Part06ItemID sql.NullInt64 + Part07ItemID sql.NullInt64 + Part08ItemID sql.NullInt64 + Part09ItemID sql.NullInt64 + Part10ItemID sql.NullInt64 + Part11ItemID sql.NullInt64 + Part12ItemID sql.NullInt64 + Part13ItemID sql.NullInt64 + Part14ItemID sql.NullInt64 + Part15ItemID sql.NullInt64 + Part16ItemID sql.NullInt64 + Part17ItemID sql.NullInt64 + Part18ItemID sql.NullInt64 + Part19ItemID sql.NullInt64 + Part20ItemID sql.NullInt64 + Part21ItemID sql.NullInt64 + Part22ItemID sql.NullInt64 + Part23ItemID sql.NullInt64 + Part00ItemTypeID int64 + Part01ItemTypeID int64 + Part02ItemTypeID int64 + Part03ItemTypeID int64 + Part04ItemTypeID int64 + Part05ItemTypeID int64 + Part06ItemTypeID int64 + Part07ItemTypeID int64 + Part08ItemTypeID int64 + Part09ItemTypeID int64 + Part10ItemTypeID int64 + Part11ItemTypeID int64 + Part12ItemTypeID int64 + Part13ItemTypeID int64 + Part14ItemTypeID int64 + Part15ItemTypeID int64 + Part16ItemTypeID int64 + Part17ItemTypeID int64 + Part18ItemTypeID int64 + Part19ItemTypeID int64 + Part20ItemTypeID int64 + Part21ItemTypeID int64 + Part22ItemTypeID int64 + Part23ItemTypeID int64 + AuxPart0ID sql.NullInt64 + AuxPart1ID sql.NullInt64 + AuxPart2ID sql.NullInt64 + AuxPart3ID sql.NullInt64 + AuxPart4ID sql.NullInt64 + CutInID sql.NullInt64 + CharacterTypeID int64 + InventoryAuxPart0TypeIDFIXNULL int64 + InventoryAuxPart1TypeIDFIXNULL int64 + InventoryAuxPart2TypeIDFIXNULL int64 + InventoryAuxPart3TypeIDFIXNULL int64 + InventoryAuxPart4TypeIDFIXNULL int64 +} + +func (q *Queries) GetCharacter(ctx context.Context, characterID int64) (GetCharacterRow, error) { row := q.db.QueryRowContext(ctx, getCharacter, characterID) - var i Character + var i GetCharacterRow err := row.Scan( &i.CharacterID, &i.PlayerID, + &i.ItemID, + &i.HairColor, + &i.Shirt, + &i.Mastery, + &i.Part00ItemID, + &i.Part01ItemID, + &i.Part02ItemID, + &i.Part03ItemID, + &i.Part04ItemID, + &i.Part05ItemID, + &i.Part06ItemID, + &i.Part07ItemID, + &i.Part08ItemID, + &i.Part09ItemID, + &i.Part10ItemID, + &i.Part11ItemID, + &i.Part12ItemID, + &i.Part13ItemID, + &i.Part14ItemID, + &i.Part15ItemID, + &i.Part16ItemID, + &i.Part17ItemID, + &i.Part18ItemID, + &i.Part19ItemID, + &i.Part20ItemID, + &i.Part21ItemID, + &i.Part22ItemID, + &i.Part23ItemID, + &i.Part00ItemTypeID, + &i.Part01ItemTypeID, + &i.Part02ItemTypeID, + &i.Part03ItemTypeID, + &i.Part04ItemTypeID, + &i.Part05ItemTypeID, + &i.Part06ItemTypeID, + &i.Part07ItemTypeID, + &i.Part08ItemTypeID, + &i.Part09ItemTypeID, + &i.Part10ItemTypeID, + &i.Part11ItemTypeID, + &i.Part12ItemTypeID, + &i.Part13ItemTypeID, + &i.Part14ItemTypeID, + &i.Part15ItemTypeID, + &i.Part16ItemTypeID, + &i.Part17ItemTypeID, + &i.Part18ItemTypeID, + &i.Part19ItemTypeID, + &i.Part20ItemTypeID, + &i.Part21ItemTypeID, + &i.Part22ItemTypeID, + &i.Part23ItemTypeID, + &i.AuxPart0ID, + &i.AuxPart1ID, + &i.AuxPart2ID, + &i.AuxPart3ID, + &i.AuxPart4ID, + &i.CutInID, &i.CharacterTypeID, - &i.CharacterData, + &i.InventoryAuxPart0TypeIDFIXNULL, + &i.InventoryAuxPart1TypeIDFIXNULL, + &i.InventoryAuxPart2TypeIDFIXNULL, + &i.InventoryAuxPart3TypeIDFIXNULL, + &i.InventoryAuxPart4TypeIDFIXNULL, ) return i, err } const getCharactersByPlayer = `-- name: GetCharactersByPlayer :many -SELECT character_id, player_id, character_type_id, character_data FROM character -WHERE player_id = ? +SELECT + character.character_id, character.player_id, character.item_id, character.hair_color, character.shirt, character.mastery, character.part00_item_id, character.part01_item_id, character.part02_item_id, character.part03_item_id, character.part04_item_id, character.part05_item_id, character.part06_item_id, character.part07_item_id, character.part08_item_id, character.part09_item_id, character.part10_item_id, character.part11_item_id, character.part12_item_id, character.part13_item_id, character.part14_item_id, character.part15_item_id, character.part16_item_id, character.part17_item_id, character.part18_item_id, character.part19_item_id, character.part20_item_id, character.part21_item_id, character.part22_item_id, character.part23_item_id, character.part00_item_type_id, character.part01_item_type_id, character.part02_item_type_id, character.part03_item_type_id, character.part04_item_type_id, character.part05_item_type_id, character.part06_item_type_id, character.part07_item_type_id, character.part08_item_type_id, character.part09_item_type_id, character.part10_item_type_id, character.part11_item_type_id, character.part12_item_type_id, character.part13_item_type_id, character.part14_item_type_id, character.part15_item_type_id, character.part16_item_type_id, character.part17_item_type_id, character.part18_item_type_id, character.part19_item_type_id, character.part20_item_type_id, character.part21_item_type_id, character.part22_item_type_id, character.part23_item_type_id, character.aux_part0_id, character.aux_part1_id, character.aux_part2_id, character.aux_part3_id, character.aux_part4_id, character.cut_in_id, + inventory_character.item_type_id AS character_type_id +FROM character AS character +LEFT JOIN inventory AS inventory_character ON (character.item_id = inventory_character.item_id) +WHERE character.player_id = ? ` -func (q *Queries) GetCharactersByPlayer(ctx context.Context, playerID int64) ([]Character, error) { +type GetCharactersByPlayerRow struct { + CharacterID int64 + PlayerID int64 + ItemID int64 + HairColor int64 + Shirt int64 + Mastery int64 + Part00ItemID sql.NullInt64 + Part01ItemID sql.NullInt64 + Part02ItemID sql.NullInt64 + Part03ItemID sql.NullInt64 + Part04ItemID sql.NullInt64 + Part05ItemID sql.NullInt64 + Part06ItemID sql.NullInt64 + Part07ItemID sql.NullInt64 + Part08ItemID sql.NullInt64 + Part09ItemID sql.NullInt64 + Part10ItemID sql.NullInt64 + Part11ItemID sql.NullInt64 + Part12ItemID sql.NullInt64 + Part13ItemID sql.NullInt64 + Part14ItemID sql.NullInt64 + Part15ItemID sql.NullInt64 + Part16ItemID sql.NullInt64 + Part17ItemID sql.NullInt64 + Part18ItemID sql.NullInt64 + Part19ItemID sql.NullInt64 + Part20ItemID sql.NullInt64 + Part21ItemID sql.NullInt64 + Part22ItemID sql.NullInt64 + Part23ItemID sql.NullInt64 + Part00ItemTypeID int64 + Part01ItemTypeID int64 + Part02ItemTypeID int64 + Part03ItemTypeID int64 + Part04ItemTypeID int64 + Part05ItemTypeID int64 + Part06ItemTypeID int64 + Part07ItemTypeID int64 + Part08ItemTypeID int64 + Part09ItemTypeID int64 + Part10ItemTypeID int64 + Part11ItemTypeID int64 + Part12ItemTypeID int64 + Part13ItemTypeID int64 + Part14ItemTypeID int64 + Part15ItemTypeID int64 + Part16ItemTypeID int64 + Part17ItemTypeID int64 + Part18ItemTypeID int64 + Part19ItemTypeID int64 + Part20ItemTypeID int64 + Part21ItemTypeID int64 + Part22ItemTypeID int64 + Part23ItemTypeID int64 + AuxPart0ID sql.NullInt64 + AuxPart1ID sql.NullInt64 + AuxPart2ID sql.NullInt64 + AuxPart3ID sql.NullInt64 + AuxPart4ID sql.NullInt64 + CutInID sql.NullInt64 + CharacterTypeID int64 +} + +func (q *Queries) GetCharactersByPlayer(ctx context.Context, playerID int64) ([]GetCharactersByPlayerRow, error) { rows, err := q.db.QueryContext(ctx, getCharactersByPlayer, playerID) if err != nil { return nil, err } defer rows.Close() - var items []Character + var items []GetCharactersByPlayerRow for rows.Next() { - var i Character + var i GetCharactersByPlayerRow if err := rows.Scan( &i.CharacterID, &i.PlayerID, + &i.ItemID, + &i.HairColor, + &i.Shirt, + &i.Mastery, + &i.Part00ItemID, + &i.Part01ItemID, + &i.Part02ItemID, + &i.Part03ItemID, + &i.Part04ItemID, + &i.Part05ItemID, + &i.Part06ItemID, + &i.Part07ItemID, + &i.Part08ItemID, + &i.Part09ItemID, + &i.Part10ItemID, + &i.Part11ItemID, + &i.Part12ItemID, + &i.Part13ItemID, + &i.Part14ItemID, + &i.Part15ItemID, + &i.Part16ItemID, + &i.Part17ItemID, + &i.Part18ItemID, + &i.Part19ItemID, + &i.Part20ItemID, + &i.Part21ItemID, + &i.Part22ItemID, + &i.Part23ItemID, + &i.Part00ItemTypeID, + &i.Part01ItemTypeID, + &i.Part02ItemTypeID, + &i.Part03ItemTypeID, + &i.Part04ItemTypeID, + &i.Part05ItemTypeID, + &i.Part06ItemTypeID, + &i.Part07ItemTypeID, + &i.Part08ItemTypeID, + &i.Part09ItemTypeID, + &i.Part10ItemTypeID, + &i.Part11ItemTypeID, + &i.Part12ItemTypeID, + &i.Part13ItemTypeID, + &i.Part14ItemTypeID, + &i.Part15ItemTypeID, + &i.Part16ItemTypeID, + &i.Part17ItemTypeID, + &i.Part18ItemTypeID, + &i.Part19ItemTypeID, + &i.Part20ItemTypeID, + &i.Part21ItemTypeID, + &i.Part22ItemTypeID, + &i.Part23ItemTypeID, + &i.AuxPart0ID, + &i.AuxPart1ID, + &i.AuxPart2ID, + &i.AuxPart3ID, + &i.AuxPart4ID, + &i.CutInID, &i.CharacterTypeID, - &i.CharacterData, ); err != nil { return nil, err } @@ -90,7 +527,7 @@ func (q *Queries) GetCharactersByPlayer(ctx context.Context, playerID int64) ([] const playerHasCharacters = `-- name: PlayerHasCharacters :one SELECT count(*) > 0 FROM character -WHERE player_id = ? +WHERE character.player_id = ? ` func (q *Queries) PlayerHasCharacters(ctx context.Context, playerID int64) (bool, error) { @@ -99,3 +536,231 @@ func (q *Queries) PlayerHasCharacters(ctx context.Context, playerID int64) (bool err := row.Scan(&column_1) return column_1, err } + +const setCharacterParts = `-- name: SetCharacterParts :one +UPDATE character +SET + part00_item_id = ?, + part01_item_id = ?, + part02_item_id = ?, + part03_item_id = ?, + part04_item_id = ?, + part05_item_id = ?, + part06_item_id = ?, + part07_item_id = ?, + part08_item_id = ?, + part09_item_id = ?, + part10_item_id = ?, + part11_item_id = ?, + part12_item_id = ?, + part13_item_id = ?, + part14_item_id = ?, + part15_item_id = ?, + part16_item_id = ?, + part17_item_id = ?, + part18_item_id = ?, + part19_item_id = ?, + part20_item_id = ?, + part21_item_id = ?, + part22_item_id = ?, + part23_item_id = ?, + part00_item_type_id = ?, + part01_item_type_id = ?, + part02_item_type_id = ?, + part03_item_type_id = ?, + part04_item_type_id = ?, + part05_item_type_id = ?, + part06_item_type_id = ?, + part07_item_type_id = ?, + part08_item_type_id = ?, + part09_item_type_id = ?, + part10_item_type_id = ?, + part11_item_type_id = ?, + part12_item_type_id = ?, + part13_item_type_id = ?, + part14_item_type_id = ?, + part15_item_type_id = ?, + part16_item_type_id = ?, + part17_item_type_id = ?, + part18_item_type_id = ?, + part19_item_type_id = ?, + part20_item_type_id = ?, + part21_item_type_id = ?, + part22_item_type_id = ?, + part23_item_type_id = ?, + cut_in_id = ? +WHERE character_id = ? +RETURNING character_id, player_id, item_id, hair_color, shirt, mastery, part00_item_id, part01_item_id, part02_item_id, part03_item_id, part04_item_id, part05_item_id, part06_item_id, part07_item_id, part08_item_id, part09_item_id, part10_item_id, part11_item_id, part12_item_id, part13_item_id, part14_item_id, part15_item_id, part16_item_id, part17_item_id, part18_item_id, part19_item_id, part20_item_id, part21_item_id, part22_item_id, part23_item_id, part00_item_type_id, part01_item_type_id, part02_item_type_id, part03_item_type_id, part04_item_type_id, part05_item_type_id, part06_item_type_id, part07_item_type_id, part08_item_type_id, part09_item_type_id, part10_item_type_id, part11_item_type_id, part12_item_type_id, part13_item_type_id, part14_item_type_id, part15_item_type_id, part16_item_type_id, part17_item_type_id, part18_item_type_id, part19_item_type_id, part20_item_type_id, part21_item_type_id, part22_item_type_id, part23_item_type_id, aux_part0_id, aux_part1_id, aux_part2_id, aux_part3_id, aux_part4_id, cut_in_id +` + +type SetCharacterPartsParams struct { + Part00ItemID sql.NullInt64 + Part01ItemID sql.NullInt64 + Part02ItemID sql.NullInt64 + Part03ItemID sql.NullInt64 + Part04ItemID sql.NullInt64 + Part05ItemID sql.NullInt64 + Part06ItemID sql.NullInt64 + Part07ItemID sql.NullInt64 + Part08ItemID sql.NullInt64 + Part09ItemID sql.NullInt64 + Part10ItemID sql.NullInt64 + Part11ItemID sql.NullInt64 + Part12ItemID sql.NullInt64 + Part13ItemID sql.NullInt64 + Part14ItemID sql.NullInt64 + Part15ItemID sql.NullInt64 + Part16ItemID sql.NullInt64 + Part17ItemID sql.NullInt64 + Part18ItemID sql.NullInt64 + Part19ItemID sql.NullInt64 + Part20ItemID sql.NullInt64 + Part21ItemID sql.NullInt64 + Part22ItemID sql.NullInt64 + Part23ItemID sql.NullInt64 + Part00ItemTypeID int64 + Part01ItemTypeID int64 + Part02ItemTypeID int64 + Part03ItemTypeID int64 + Part04ItemTypeID int64 + Part05ItemTypeID int64 + Part06ItemTypeID int64 + Part07ItemTypeID int64 + Part08ItemTypeID int64 + Part09ItemTypeID int64 + Part10ItemTypeID int64 + Part11ItemTypeID int64 + Part12ItemTypeID int64 + Part13ItemTypeID int64 + Part14ItemTypeID int64 + Part15ItemTypeID int64 + Part16ItemTypeID int64 + Part17ItemTypeID int64 + Part18ItemTypeID int64 + Part19ItemTypeID int64 + Part20ItemTypeID int64 + Part21ItemTypeID int64 + Part22ItemTypeID int64 + Part23ItemTypeID int64 + CutInID sql.NullInt64 + CharacterID int64 +} + +func (q *Queries) SetCharacterParts(ctx context.Context, arg SetCharacterPartsParams) (Character, error) { + row := q.db.QueryRowContext(ctx, setCharacterParts, + arg.Part00ItemID, + arg.Part01ItemID, + arg.Part02ItemID, + arg.Part03ItemID, + arg.Part04ItemID, + arg.Part05ItemID, + arg.Part06ItemID, + arg.Part07ItemID, + arg.Part08ItemID, + arg.Part09ItemID, + arg.Part10ItemID, + arg.Part11ItemID, + arg.Part12ItemID, + arg.Part13ItemID, + arg.Part14ItemID, + arg.Part15ItemID, + arg.Part16ItemID, + arg.Part17ItemID, + arg.Part18ItemID, + arg.Part19ItemID, + arg.Part20ItemID, + arg.Part21ItemID, + arg.Part22ItemID, + arg.Part23ItemID, + arg.Part00ItemTypeID, + arg.Part01ItemTypeID, + arg.Part02ItemTypeID, + arg.Part03ItemTypeID, + arg.Part04ItemTypeID, + arg.Part05ItemTypeID, + arg.Part06ItemTypeID, + arg.Part07ItemTypeID, + arg.Part08ItemTypeID, + arg.Part09ItemTypeID, + arg.Part10ItemTypeID, + arg.Part11ItemTypeID, + arg.Part12ItemTypeID, + arg.Part13ItemTypeID, + arg.Part14ItemTypeID, + arg.Part15ItemTypeID, + arg.Part16ItemTypeID, + arg.Part17ItemTypeID, + arg.Part18ItemTypeID, + arg.Part19ItemTypeID, + arg.Part20ItemTypeID, + arg.Part21ItemTypeID, + arg.Part22ItemTypeID, + arg.Part23ItemTypeID, + arg.CutInID, + arg.CharacterID, + ) + var i Character + err := row.Scan( + &i.CharacterID, + &i.PlayerID, + &i.ItemID, + &i.HairColor, + &i.Shirt, + &i.Mastery, + &i.Part00ItemID, + &i.Part01ItemID, + &i.Part02ItemID, + &i.Part03ItemID, + &i.Part04ItemID, + &i.Part05ItemID, + &i.Part06ItemID, + &i.Part07ItemID, + &i.Part08ItemID, + &i.Part09ItemID, + &i.Part10ItemID, + &i.Part11ItemID, + &i.Part12ItemID, + &i.Part13ItemID, + &i.Part14ItemID, + &i.Part15ItemID, + &i.Part16ItemID, + &i.Part17ItemID, + &i.Part18ItemID, + &i.Part19ItemID, + &i.Part20ItemID, + &i.Part21ItemID, + &i.Part22ItemID, + &i.Part23ItemID, + &i.Part00ItemTypeID, + &i.Part01ItemTypeID, + &i.Part02ItemTypeID, + &i.Part03ItemTypeID, + &i.Part04ItemTypeID, + &i.Part05ItemTypeID, + &i.Part06ItemTypeID, + &i.Part07ItemTypeID, + &i.Part08ItemTypeID, + &i.Part09ItemTypeID, + &i.Part10ItemTypeID, + &i.Part11ItemTypeID, + &i.Part12ItemTypeID, + &i.Part13ItemTypeID, + &i.Part14ItemTypeID, + &i.Part15ItemTypeID, + &i.Part16ItemTypeID, + &i.Part17ItemTypeID, + &i.Part18ItemTypeID, + &i.Part19ItemTypeID, + &i.Part20ItemTypeID, + &i.Part21ItemTypeID, + &i.Part22ItemTypeID, + &i.Part23ItemTypeID, + &i.AuxPart0ID, + &i.AuxPart1ID, + &i.AuxPart2ID, + &i.AuxPart3ID, + &i.AuxPart4ID, + &i.CutInID, + ) + return i, err +} diff --git a/gen/dbmodels/inventory.sql.go b/gen/dbmodels/inventory.sql.go new file mode 100644 index 0000000..33be4fc --- /dev/null +++ b/gen/dbmodels/inventory.sql.go @@ -0,0 +1,168 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.18.0 +// source: inventory.sql + +package dbmodels + +import ( + "context" + "database/sql" +) + +const addItemToInventory = `-- name: AddItemToInventory :one +INSERT INTO inventory ( + player_id, + item_type_id, + quantity +) VALUES ( + ?, + ?, + ? +) +RETURNING item_id, player_id, item_type_id, quantity +` + +type AddItemToInventoryParams struct { + PlayerID int64 + ItemTypeID int64 + Quantity sql.NullInt64 +} + +func (q *Queries) AddItemToInventory(ctx context.Context, arg AddItemToInventoryParams) (Inventory, error) { + row := q.db.QueryRowContext(ctx, addItemToInventory, arg.PlayerID, arg.ItemTypeID, arg.Quantity) + var i Inventory + err := row.Scan( + &i.ItemID, + &i.PlayerID, + &i.ItemTypeID, + &i.Quantity, + ) + return i, err +} + +const getItem = `-- name: GetItem :one +SELECT item_id, player_id, item_type_id, quantity FROM inventory WHERE player_id = ? AND item_id = ? +` + +type GetItemParams struct { + PlayerID int64 + ItemID int64 +} + +func (q *Queries) GetItem(ctx context.Context, arg GetItemParams) (Inventory, error) { + row := q.db.QueryRowContext(ctx, getItem, arg.PlayerID, arg.ItemID) + var i Inventory + err := row.Scan( + &i.ItemID, + &i.PlayerID, + &i.ItemTypeID, + &i.Quantity, + ) + return i, err +} + +const getItemsByTypeID = `-- name: GetItemsByTypeID :many +SELECT item_id, player_id, item_type_id, quantity FROM inventory WHERE player_id = ? AND item_type_id = ? +` + +type GetItemsByTypeIDParams struct { + PlayerID int64 + ItemTypeID int64 +} + +func (q *Queries) GetItemsByTypeID(ctx context.Context, arg GetItemsByTypeIDParams) ([]Inventory, error) { + rows, err := q.db.QueryContext(ctx, getItemsByTypeID, arg.PlayerID, arg.ItemTypeID) + if err != nil { + return nil, err + } + defer rows.Close() + var items []Inventory + for rows.Next() { + var i Inventory + if err := rows.Scan( + &i.ItemID, + &i.PlayerID, + &i.ItemTypeID, + &i.Quantity, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const getPlayerInventory = `-- name: GetPlayerInventory :many +SELECT item_id, player_id, item_type_id, quantity FROM inventory WHERE player_id = ? +` + +func (q *Queries) GetPlayerInventory(ctx context.Context, playerID int64) ([]Inventory, error) { + rows, err := q.db.QueryContext(ctx, getPlayerInventory, playerID) + if err != nil { + return nil, err + } + defer rows.Close() + var items []Inventory + for rows.Next() { + var i Inventory + if err := rows.Scan( + &i.ItemID, + &i.PlayerID, + &i.ItemTypeID, + &i.Quantity, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const removeItemFromInventory = `-- name: RemoveItemFromInventory :exec +DELETE FROM inventory WHERE player_id = ? AND item_id = ? +` + +type RemoveItemFromInventoryParams struct { + PlayerID int64 + ItemID int64 +} + +func (q *Queries) RemoveItemFromInventory(ctx context.Context, arg RemoveItemFromInventoryParams) error { + _, err := q.db.ExecContext(ctx, removeItemFromInventory, arg.PlayerID, arg.ItemID) + return err +} + +const setItemQuantity = `-- name: SetItemQuantity :one +UPDATE inventory SET quantity = ? WHERE player_id = ? AND item_id = ? RETURNING item_id, player_id, item_type_id, quantity +` + +type SetItemQuantityParams struct { + Quantity sql.NullInt64 + PlayerID int64 + ItemID int64 +} + +func (q *Queries) SetItemQuantity(ctx context.Context, arg SetItemQuantityParams) (Inventory, error) { + row := q.db.QueryRowContext(ctx, setItemQuantity, arg.Quantity, arg.PlayerID, arg.ItemID) + var i Inventory + err := row.Scan( + &i.ItemID, + &i.PlayerID, + &i.ItemTypeID, + &i.Quantity, + ) + return i, err +} diff --git a/gen/dbmodels/models.go b/gen/dbmodels/models.go index dcb3b4e..c74707d 100644 --- a/gen/dbmodels/models.go +++ b/gen/dbmodels/models.go @@ -9,10 +9,73 @@ import ( ) type Character struct { - CharacterID int64 - PlayerID int64 - CharacterTypeID int64 - CharacterData []byte + CharacterID int64 + PlayerID int64 + ItemID int64 + HairColor int64 + Shirt int64 + Mastery int64 + Part00ItemID sql.NullInt64 + Part01ItemID sql.NullInt64 + Part02ItemID sql.NullInt64 + Part03ItemID sql.NullInt64 + Part04ItemID sql.NullInt64 + Part05ItemID sql.NullInt64 + Part06ItemID sql.NullInt64 + Part07ItemID sql.NullInt64 + Part08ItemID sql.NullInt64 + Part09ItemID sql.NullInt64 + Part10ItemID sql.NullInt64 + Part11ItemID sql.NullInt64 + Part12ItemID sql.NullInt64 + Part13ItemID sql.NullInt64 + Part14ItemID sql.NullInt64 + Part15ItemID sql.NullInt64 + Part16ItemID sql.NullInt64 + Part17ItemID sql.NullInt64 + Part18ItemID sql.NullInt64 + Part19ItemID sql.NullInt64 + Part20ItemID sql.NullInt64 + Part21ItemID sql.NullInt64 + Part22ItemID sql.NullInt64 + Part23ItemID sql.NullInt64 + Part00ItemTypeID int64 + Part01ItemTypeID int64 + Part02ItemTypeID int64 + Part03ItemTypeID int64 + Part04ItemTypeID int64 + Part05ItemTypeID int64 + Part06ItemTypeID int64 + Part07ItemTypeID int64 + Part08ItemTypeID int64 + Part09ItemTypeID int64 + Part10ItemTypeID int64 + Part11ItemTypeID int64 + Part12ItemTypeID int64 + Part13ItemTypeID int64 + Part14ItemTypeID int64 + Part15ItemTypeID int64 + Part16ItemTypeID int64 + Part17ItemTypeID int64 + Part18ItemTypeID int64 + Part19ItemTypeID int64 + Part20ItemTypeID int64 + Part21ItemTypeID int64 + Part22ItemTypeID int64 + Part23ItemTypeID int64 + AuxPart0ID sql.NullInt64 + AuxPart1ID sql.NullInt64 + AuxPart2ID sql.NullInt64 + AuxPart3ID sql.NullInt64 + AuxPart4ID sql.NullInt64 + CutInID sql.NullInt64 +} + +type Inventory struct { + ItemID int64 + PlayerID int64 + ItemTypeID int64 + Quantity sql.NullInt64 } type Player struct { @@ -21,7 +84,31 @@ type Player struct { Nickname sql.NullString PasswordHash string Pang int64 + Points int64 Rank int64 + BallTypeID int64 + MascotTypeID int64 + Slot0TypeID int64 + Slot1TypeID int64 + Slot2TypeID int64 + Slot3TypeID int64 + Slot4TypeID int64 + Slot5TypeID int64 + Slot6TypeID int64 + Slot7TypeID int64 + Slot8TypeID int64 + Slot9TypeID int64 + CaddieID sql.NullInt64 + ClubID sql.NullInt64 + BackgroundID sql.NullInt64 + FrameID sql.NullInt64 + StickerID sql.NullInt64 + SlotID sql.NullInt64 + CutInID sql.NullInt64 + TitleID sql.NullInt64 + Poster0ID sql.NullInt64 + Poster1ID sql.NullInt64 + CharacterID sql.NullInt64 } type Session struct { diff --git a/gen/dbmodels/player.sql.go b/gen/dbmodels/player.sql.go index bdb142f..705071a 100644 --- a/gen/dbmodels/player.sql.go +++ b/gen/dbmodels/player.sql.go @@ -18,7 +18,7 @@ INSERT INTO player ( ) VALUES ( ?, ?, ? ) -RETURNING player_id, username, nickname, password_hash, pang, rank +RETURNING player_id, username, nickname, password_hash, pang, points, rank, ball_type_id, mascot_type_id, slot0_type_id, slot1_type_id, slot2_type_id, slot3_type_id, slot4_type_id, slot5_type_id, slot6_type_id, slot7_type_id, slot8_type_id, slot9_type_id, caddie_id, club_id, background_id, frame_id, sticker_id, slot_id, cut_in_id, title_id, poster0_id, poster1_id, character_id ` type CreatePlayerParams struct { @@ -36,33 +36,284 @@ func (q *Queries) CreatePlayer(ctx context.Context, arg CreatePlayerParams) (Pla &i.Nickname, &i.PasswordHash, &i.Pang, + &i.Points, &i.Rank, + &i.BallTypeID, + &i.MascotTypeID, + &i.Slot0TypeID, + &i.Slot1TypeID, + &i.Slot2TypeID, + &i.Slot3TypeID, + &i.Slot4TypeID, + &i.Slot5TypeID, + &i.Slot6TypeID, + &i.Slot7TypeID, + &i.Slot8TypeID, + &i.Slot9TypeID, + &i.CaddieID, + &i.ClubID, + &i.BackgroundID, + &i.FrameID, + &i.StickerID, + &i.SlotID, + &i.CutInID, + &i.TitleID, + &i.Poster0ID, + &i.Poster1ID, + &i.CharacterID, ) return i, err } const getPlayer = `-- name: GetPlayer :one -SELECT player_id, username, nickname, password_hash, pang, rank FROM player -WHERE player_id = ? LIMIT 1 +SELECT + player.player_id, player.username, player.nickname, player.password_hash, player.pang, player.points, player.rank, player.ball_type_id, player.mascot_type_id, player.slot0_type_id, player.slot1_type_id, player.slot2_type_id, player.slot3_type_id, player.slot4_type_id, player.slot5_type_id, player.slot6_type_id, player.slot7_type_id, player.slot8_type_id, player.slot9_type_id, player.caddie_id, player.club_id, player.background_id, player.frame_id, player.sticker_id, player.slot_id, player.cut_in_id, player.title_id, player.poster0_id, player.poster1_id, player.character_id, + character.character_id, character.player_id, character.item_id, character.hair_color, character.shirt, character.mastery, character.part00_item_id, character.part01_item_id, character.part02_item_id, character.part03_item_id, character.part04_item_id, character.part05_item_id, character.part06_item_id, character.part07_item_id, character.part08_item_id, character.part09_item_id, character.part10_item_id, character.part11_item_id, character.part12_item_id, character.part13_item_id, character.part14_item_id, character.part15_item_id, character.part16_item_id, character.part17_item_id, character.part18_item_id, character.part19_item_id, character.part20_item_id, character.part21_item_id, character.part22_item_id, character.part23_item_id, character.part00_item_type_id, character.part01_item_type_id, character.part02_item_type_id, character.part03_item_type_id, character.part04_item_type_id, character.part05_item_type_id, character.part06_item_type_id, character.part07_item_type_id, character.part08_item_type_id, character.part09_item_type_id, character.part10_item_type_id, character.part11_item_type_id, character.part12_item_type_id, character.part13_item_type_id, character.part14_item_type_id, character.part15_item_type_id, character.part16_item_type_id, character.part17_item_type_id, character.part18_item_type_id, character.part19_item_type_id, character.part20_item_type_id, character.part21_item_type_id, character.part22_item_type_id, character.part23_item_type_id, character.aux_part0_id, character.aux_part1_id, character.aux_part2_id, character.aux_part3_id, character.aux_part4_id, character.cut_in_id, + inventory_character.item_type_id AS character_type_id_, + inventory_caddie.item_type_id AS caddie_type_id_, + inventory_club.item_type_id AS club_type_id_, + inventory_background.item_type_id AS background_type_id_, + inventory_frame.item_type_id AS frame_type_id_, + inventory_sticker.item_type_id AS sticker_type_id_, + inventory_slot.item_type_id AS slot_type_id_, + inventory_cut_in.item_type_id AS cut_in_type_id_, + inventory_title.item_type_id AS title_type_id_, + inventory_poster0.item_type_id AS poster0_type_id_, + inventory_poster1.item_type_id AS poster1_type_id_ +FROM player AS player +LEFT JOIN character USING (character_id) +LEFT JOIN inventory AS inventory_character ON (character.item_id = inventory_character.item_id) +LEFT JOIN inventory AS inventory_caddie ON (player.caddie_id = inventory_caddie.item_id) +LEFT JOIN inventory AS inventory_club ON (player.club_id = inventory_club.item_id) +LEFT JOIN inventory AS inventory_background ON (player.background_id = inventory_background.item_id) +LEFT JOIN inventory AS inventory_frame ON (player.frame_id = inventory_frame.item_id) +LEFT JOIN inventory AS inventory_sticker ON (player.sticker_id = inventory_sticker.item_id) +LEFT JOIN inventory AS inventory_slot ON (player.slot_id = inventory_slot.item_id) +LEFT JOIN inventory AS inventory_cut_in ON (player.cut_in_id = inventory_cut_in.item_id) +LEFT JOIN inventory AS inventory_title ON (player.title_id = inventory_title.item_id) +LEFT JOIN inventory AS inventory_poster0 ON (player.poster0_id = inventory_poster0.item_id) +LEFT JOIN inventory AS inventory_poster1 ON (player.poster1_id = inventory_poster1.item_id) +WHERE player.player_id = ? +LIMIT 1 ` -func (q *Queries) GetPlayer(ctx context.Context, playerID int64) (Player, error) { +type GetPlayerRow struct { + PlayerID int64 + Username string + Nickname sql.NullString + PasswordHash string + Pang int64 + Points int64 + Rank int64 + BallTypeID int64 + MascotTypeID int64 + Slot0TypeID int64 + Slot1TypeID int64 + Slot2TypeID int64 + Slot3TypeID int64 + Slot4TypeID int64 + Slot5TypeID int64 + Slot6TypeID int64 + Slot7TypeID int64 + Slot8TypeID int64 + Slot9TypeID int64 + CaddieID sql.NullInt64 + ClubID sql.NullInt64 + BackgroundID sql.NullInt64 + FrameID sql.NullInt64 + StickerID sql.NullInt64 + SlotID sql.NullInt64 + CutInID sql.NullInt64 + TitleID sql.NullInt64 + Poster0ID sql.NullInt64 + Poster1ID sql.NullInt64 + CharacterID sql.NullInt64 + CharacterID_2 int64 + PlayerID_2 int64 + ItemID int64 + HairColor int64 + Shirt int64 + Mastery int64 + Part00ItemID sql.NullInt64 + Part01ItemID sql.NullInt64 + Part02ItemID sql.NullInt64 + Part03ItemID sql.NullInt64 + Part04ItemID sql.NullInt64 + Part05ItemID sql.NullInt64 + Part06ItemID sql.NullInt64 + Part07ItemID sql.NullInt64 + Part08ItemID sql.NullInt64 + Part09ItemID sql.NullInt64 + Part10ItemID sql.NullInt64 + Part11ItemID sql.NullInt64 + Part12ItemID sql.NullInt64 + Part13ItemID sql.NullInt64 + Part14ItemID sql.NullInt64 + Part15ItemID sql.NullInt64 + Part16ItemID sql.NullInt64 + Part17ItemID sql.NullInt64 + Part18ItemID sql.NullInt64 + Part19ItemID sql.NullInt64 + Part20ItemID sql.NullInt64 + Part21ItemID sql.NullInt64 + Part22ItemID sql.NullInt64 + Part23ItemID sql.NullInt64 + Part00ItemTypeID int64 + Part01ItemTypeID int64 + Part02ItemTypeID int64 + Part03ItemTypeID int64 + Part04ItemTypeID int64 + Part05ItemTypeID int64 + Part06ItemTypeID int64 + Part07ItemTypeID int64 + Part08ItemTypeID int64 + Part09ItemTypeID int64 + Part10ItemTypeID int64 + Part11ItemTypeID int64 + Part12ItemTypeID int64 + Part13ItemTypeID int64 + Part14ItemTypeID int64 + Part15ItemTypeID int64 + Part16ItemTypeID int64 + Part17ItemTypeID int64 + Part18ItemTypeID int64 + Part19ItemTypeID int64 + Part20ItemTypeID int64 + Part21ItemTypeID int64 + Part22ItemTypeID int64 + Part23ItemTypeID int64 + AuxPart0ID sql.NullInt64 + AuxPart1ID sql.NullInt64 + AuxPart2ID sql.NullInt64 + AuxPart3ID sql.NullInt64 + AuxPart4ID sql.NullInt64 + CutInID_2 sql.NullInt64 + CharacterTypeID sql.NullInt64 + CaddieTypeID sql.NullInt64 + ClubTypeID sql.NullInt64 + BackgroundTypeID sql.NullInt64 + FrameTypeID sql.NullInt64 + StickerTypeID sql.NullInt64 + SlotTypeID sql.NullInt64 + CutInTypeID sql.NullInt64 + TitleTypeID sql.NullInt64 + Poster0TypeID sql.NullInt64 + Poster1TypeID sql.NullInt64 +} + +func (q *Queries) GetPlayer(ctx context.Context, playerID int64) (GetPlayerRow, error) { row := q.db.QueryRowContext(ctx, getPlayer, playerID) - var i Player + var i GetPlayerRow err := row.Scan( &i.PlayerID, &i.Username, &i.Nickname, &i.PasswordHash, &i.Pang, + &i.Points, &i.Rank, + &i.BallTypeID, + &i.MascotTypeID, + &i.Slot0TypeID, + &i.Slot1TypeID, + &i.Slot2TypeID, + &i.Slot3TypeID, + &i.Slot4TypeID, + &i.Slot5TypeID, + &i.Slot6TypeID, + &i.Slot7TypeID, + &i.Slot8TypeID, + &i.Slot9TypeID, + &i.CaddieID, + &i.ClubID, + &i.BackgroundID, + &i.FrameID, + &i.StickerID, + &i.SlotID, + &i.CutInID, + &i.TitleID, + &i.Poster0ID, + &i.Poster1ID, + &i.CharacterID, + &i.CharacterID_2, + &i.PlayerID_2, + &i.ItemID, + &i.HairColor, + &i.Shirt, + &i.Mastery, + &i.Part00ItemID, + &i.Part01ItemID, + &i.Part02ItemID, + &i.Part03ItemID, + &i.Part04ItemID, + &i.Part05ItemID, + &i.Part06ItemID, + &i.Part07ItemID, + &i.Part08ItemID, + &i.Part09ItemID, + &i.Part10ItemID, + &i.Part11ItemID, + &i.Part12ItemID, + &i.Part13ItemID, + &i.Part14ItemID, + &i.Part15ItemID, + &i.Part16ItemID, + &i.Part17ItemID, + &i.Part18ItemID, + &i.Part19ItemID, + &i.Part20ItemID, + &i.Part21ItemID, + &i.Part22ItemID, + &i.Part23ItemID, + &i.Part00ItemTypeID, + &i.Part01ItemTypeID, + &i.Part02ItemTypeID, + &i.Part03ItemTypeID, + &i.Part04ItemTypeID, + &i.Part05ItemTypeID, + &i.Part06ItemTypeID, + &i.Part07ItemTypeID, + &i.Part08ItemTypeID, + &i.Part09ItemTypeID, + &i.Part10ItemTypeID, + &i.Part11ItemTypeID, + &i.Part12ItemTypeID, + &i.Part13ItemTypeID, + &i.Part14ItemTypeID, + &i.Part15ItemTypeID, + &i.Part16ItemTypeID, + &i.Part17ItemTypeID, + &i.Part18ItemTypeID, + &i.Part19ItemTypeID, + &i.Part20ItemTypeID, + &i.Part21ItemTypeID, + &i.Part22ItemTypeID, + &i.Part23ItemTypeID, + &i.AuxPart0ID, + &i.AuxPart1ID, + &i.AuxPart2ID, + &i.AuxPart3ID, + &i.AuxPart4ID, + &i.CutInID_2, + &i.CharacterTypeID, + &i.CaddieTypeID, + &i.ClubTypeID, + &i.BackgroundTypeID, + &i.FrameTypeID, + &i.StickerTypeID, + &i.SlotTypeID, + &i.CutInTypeID, + &i.TitleTypeID, + &i.Poster0TypeID, + &i.Poster1TypeID, ) return i, err } const getPlayerByUsername = `-- name: GetPlayerByUsername :one -SELECT player_id, username, nickname, password_hash, pang, rank FROM player -WHERE username = ? LIMIT 1 +SELECT player_id, username, nickname, password_hash, pang, points, rank, ball_type_id, mascot_type_id, slot0_type_id, slot1_type_id, slot2_type_id, slot3_type_id, slot4_type_id, slot5_type_id, slot6_type_id, slot7_type_id, slot8_type_id, slot9_type_id, caddie_id, club_id, background_id, frame_id, sticker_id, slot_id, cut_in_id, title_id, poster0_id, poster1_id, character_id FROM player +WHERE username = ? +LIMIT 1 ` func (q *Queries) GetPlayerByUsername(ctx context.Context, username string) (Player, error) { @@ -74,13 +325,460 @@ func (q *Queries) GetPlayerByUsername(ctx context.Context, username string) (Pla &i.Nickname, &i.PasswordHash, &i.Pang, + &i.Points, + &i.Rank, + &i.BallTypeID, + &i.MascotTypeID, + &i.Slot0TypeID, + &i.Slot1TypeID, + &i.Slot2TypeID, + &i.Slot3TypeID, + &i.Slot4TypeID, + &i.Slot5TypeID, + &i.Slot6TypeID, + &i.Slot7TypeID, + &i.Slot8TypeID, + &i.Slot9TypeID, + &i.CaddieID, + &i.ClubID, + &i.BackgroundID, + &i.FrameID, + &i.StickerID, + &i.SlotID, + &i.CutInID, + &i.TitleID, + &i.Poster0ID, + &i.Poster1ID, + &i.CharacterID, + ) + return i, err +} + +const getPlayerConsumables = `-- name: GetPlayerConsumables :one +SELECT + slot0_type_id, + slot1_type_id, + slot2_type_id, + slot3_type_id, + slot4_type_id, + slot5_type_id, + slot6_type_id, + slot7_type_id, + slot8_type_id, + slot9_type_id +FROM player +WHERE player_id = ? +` + +type GetPlayerConsumablesRow struct { + Slot0TypeID int64 + Slot1TypeID int64 + Slot2TypeID int64 + Slot3TypeID int64 + Slot4TypeID int64 + Slot5TypeID int64 + Slot6TypeID int64 + Slot7TypeID int64 + Slot8TypeID int64 + Slot9TypeID int64 +} + +func (q *Queries) GetPlayerConsumables(ctx context.Context, playerID int64) (GetPlayerConsumablesRow, error) { + row := q.db.QueryRowContext(ctx, getPlayerConsumables, playerID) + var i GetPlayerConsumablesRow + err := row.Scan( + &i.Slot0TypeID, + &i.Slot1TypeID, + &i.Slot2TypeID, + &i.Slot3TypeID, + &i.Slot4TypeID, + &i.Slot5TypeID, + &i.Slot6TypeID, + &i.Slot7TypeID, + &i.Slot8TypeID, + &i.Slot9TypeID, + ) + return i, err +} + +const getPlayerCurrency = `-- name: GetPlayerCurrency :one +SELECT pang, points FROM player WHERE player_id = ? +` + +type GetPlayerCurrencyRow struct { + Pang int64 + Points int64 +} + +func (q *Queries) GetPlayerCurrency(ctx context.Context, playerID int64) (GetPlayerCurrencyRow, error) { + row := q.db.QueryRowContext(ctx, getPlayerCurrency, playerID) + var i GetPlayerCurrencyRow + err := row.Scan(&i.Pang, &i.Points) + return i, err +} + +const setPlayerCaddie = `-- name: SetPlayerCaddie :one +UPDATE player SET caddie_id = ? WHERE player_id = ? RETURNING player_id, username, nickname, password_hash, pang, points, rank, ball_type_id, mascot_type_id, slot0_type_id, slot1_type_id, slot2_type_id, slot3_type_id, slot4_type_id, slot5_type_id, slot6_type_id, slot7_type_id, slot8_type_id, slot9_type_id, caddie_id, club_id, background_id, frame_id, sticker_id, slot_id, cut_in_id, title_id, poster0_id, poster1_id, character_id +` + +type SetPlayerCaddieParams struct { + CaddieID sql.NullInt64 + PlayerID int64 +} + +func (q *Queries) SetPlayerCaddie(ctx context.Context, arg SetPlayerCaddieParams) (Player, error) { + row := q.db.QueryRowContext(ctx, setPlayerCaddie, arg.CaddieID, arg.PlayerID) + var i Player + err := row.Scan( + &i.PlayerID, + &i.Username, + &i.Nickname, + &i.PasswordHash, + &i.Pang, + &i.Points, + &i.Rank, + &i.BallTypeID, + &i.MascotTypeID, + &i.Slot0TypeID, + &i.Slot1TypeID, + &i.Slot2TypeID, + &i.Slot3TypeID, + &i.Slot4TypeID, + &i.Slot5TypeID, + &i.Slot6TypeID, + &i.Slot7TypeID, + &i.Slot8TypeID, + &i.Slot9TypeID, + &i.CaddieID, + &i.ClubID, + &i.BackgroundID, + &i.FrameID, + &i.StickerID, + &i.SlotID, + &i.CutInID, + &i.TitleID, + &i.Poster0ID, + &i.Poster1ID, + &i.CharacterID, + ) + return i, err +} + +const setPlayerCharacter = `-- name: SetPlayerCharacter :one +UPDATE player SET character_id = ? WHERE player_id = ? RETURNING player_id, username, nickname, password_hash, pang, points, rank, ball_type_id, mascot_type_id, slot0_type_id, slot1_type_id, slot2_type_id, slot3_type_id, slot4_type_id, slot5_type_id, slot6_type_id, slot7_type_id, slot8_type_id, slot9_type_id, caddie_id, club_id, background_id, frame_id, sticker_id, slot_id, cut_in_id, title_id, poster0_id, poster1_id, character_id +` + +type SetPlayerCharacterParams struct { + CharacterID sql.NullInt64 + PlayerID int64 +} + +func (q *Queries) SetPlayerCharacter(ctx context.Context, arg SetPlayerCharacterParams) (Player, error) { + row := q.db.QueryRowContext(ctx, setPlayerCharacter, arg.CharacterID, arg.PlayerID) + var i Player + err := row.Scan( + &i.PlayerID, + &i.Username, + &i.Nickname, + &i.PasswordHash, + &i.Pang, + &i.Points, + &i.Rank, + &i.BallTypeID, + &i.MascotTypeID, + &i.Slot0TypeID, + &i.Slot1TypeID, + &i.Slot2TypeID, + &i.Slot3TypeID, + &i.Slot4TypeID, + &i.Slot5TypeID, + &i.Slot6TypeID, + &i.Slot7TypeID, + &i.Slot8TypeID, + &i.Slot9TypeID, + &i.CaddieID, + &i.ClubID, + &i.BackgroundID, + &i.FrameID, + &i.StickerID, + &i.SlotID, + &i.CutInID, + &i.TitleID, + &i.Poster0ID, + &i.Poster1ID, + &i.CharacterID, + ) + return i, err +} + +const setPlayerClubSet = `-- name: SetPlayerClubSet :one +UPDATE player SET club_id = ? WHERE player_id = ? RETURNING player_id, username, nickname, password_hash, pang, points, rank, ball_type_id, mascot_type_id, slot0_type_id, slot1_type_id, slot2_type_id, slot3_type_id, slot4_type_id, slot5_type_id, slot6_type_id, slot7_type_id, slot8_type_id, slot9_type_id, caddie_id, club_id, background_id, frame_id, sticker_id, slot_id, cut_in_id, title_id, poster0_id, poster1_id, character_id +` + +type SetPlayerClubSetParams struct { + ClubID sql.NullInt64 + PlayerID int64 +} + +func (q *Queries) SetPlayerClubSet(ctx context.Context, arg SetPlayerClubSetParams) (Player, error) { + row := q.db.QueryRowContext(ctx, setPlayerClubSet, arg.ClubID, arg.PlayerID) + var i Player + err := row.Scan( + &i.PlayerID, + &i.Username, + &i.Nickname, + &i.PasswordHash, + &i.Pang, + &i.Points, + &i.Rank, + &i.BallTypeID, + &i.MascotTypeID, + &i.Slot0TypeID, + &i.Slot1TypeID, + &i.Slot2TypeID, + &i.Slot3TypeID, + &i.Slot4TypeID, + &i.Slot5TypeID, + &i.Slot6TypeID, + &i.Slot7TypeID, + &i.Slot8TypeID, + &i.Slot9TypeID, + &i.CaddieID, + &i.ClubID, + &i.BackgroundID, + &i.FrameID, + &i.StickerID, + &i.SlotID, + &i.CutInID, + &i.TitleID, + &i.Poster0ID, + &i.Poster1ID, + &i.CharacterID, + ) + return i, err +} + +const setPlayerComet = `-- name: SetPlayerComet :one +UPDATE player SET ball_type_id = ? WHERE player_id = ? RETURNING player_id, username, nickname, password_hash, pang, points, rank, ball_type_id, mascot_type_id, slot0_type_id, slot1_type_id, slot2_type_id, slot3_type_id, slot4_type_id, slot5_type_id, slot6_type_id, slot7_type_id, slot8_type_id, slot9_type_id, caddie_id, club_id, background_id, frame_id, sticker_id, slot_id, cut_in_id, title_id, poster0_id, poster1_id, character_id +` + +type SetPlayerCometParams struct { + BallTypeID int64 + PlayerID int64 +} + +func (q *Queries) SetPlayerComet(ctx context.Context, arg SetPlayerCometParams) (Player, error) { + row := q.db.QueryRowContext(ctx, setPlayerComet, arg.BallTypeID, arg.PlayerID) + var i Player + err := row.Scan( + &i.PlayerID, + &i.Username, + &i.Nickname, + &i.PasswordHash, + &i.Pang, + &i.Points, + &i.Rank, + &i.BallTypeID, + &i.MascotTypeID, + &i.Slot0TypeID, + &i.Slot1TypeID, + &i.Slot2TypeID, + &i.Slot3TypeID, + &i.Slot4TypeID, + &i.Slot5TypeID, + &i.Slot6TypeID, + &i.Slot7TypeID, + &i.Slot8TypeID, + &i.Slot9TypeID, + &i.CaddieID, + &i.ClubID, + &i.BackgroundID, + &i.FrameID, + &i.StickerID, + &i.SlotID, + &i.CutInID, + &i.TitleID, + &i.Poster0ID, + &i.Poster1ID, + &i.CharacterID, + ) + return i, err +} + +const setPlayerConsumables = `-- name: SetPlayerConsumables :one +UPDATE player +SET + slot0_type_id = ?, + slot1_type_id = ?, + slot2_type_id = ?, + slot3_type_id = ?, + slot4_type_id = ?, + slot5_type_id = ?, + slot6_type_id = ?, + slot7_type_id = ?, + slot8_type_id = ?, + slot9_type_id = ? +WHERE player_id = ? +RETURNING player_id, username, nickname, password_hash, pang, points, rank, ball_type_id, mascot_type_id, slot0_type_id, slot1_type_id, slot2_type_id, slot3_type_id, slot4_type_id, slot5_type_id, slot6_type_id, slot7_type_id, slot8_type_id, slot9_type_id, caddie_id, club_id, background_id, frame_id, sticker_id, slot_id, cut_in_id, title_id, poster0_id, poster1_id, character_id +` + +type SetPlayerConsumablesParams struct { + Slot0TypeID int64 + Slot1TypeID int64 + Slot2TypeID int64 + Slot3TypeID int64 + Slot4TypeID int64 + Slot5TypeID int64 + Slot6TypeID int64 + Slot7TypeID int64 + Slot8TypeID int64 + Slot9TypeID int64 + PlayerID int64 +} + +func (q *Queries) SetPlayerConsumables(ctx context.Context, arg SetPlayerConsumablesParams) (Player, error) { + row := q.db.QueryRowContext(ctx, setPlayerConsumables, + arg.Slot0TypeID, + arg.Slot1TypeID, + arg.Slot2TypeID, + arg.Slot3TypeID, + arg.Slot4TypeID, + arg.Slot5TypeID, + arg.Slot6TypeID, + arg.Slot7TypeID, + arg.Slot8TypeID, + arg.Slot9TypeID, + arg.PlayerID, + ) + var i Player + err := row.Scan( + &i.PlayerID, + &i.Username, + &i.Nickname, + &i.PasswordHash, + &i.Pang, + &i.Points, + &i.Rank, + &i.BallTypeID, + &i.MascotTypeID, + &i.Slot0TypeID, + &i.Slot1TypeID, + &i.Slot2TypeID, + &i.Slot3TypeID, + &i.Slot4TypeID, + &i.Slot5TypeID, + &i.Slot6TypeID, + &i.Slot7TypeID, + &i.Slot8TypeID, + &i.Slot9TypeID, + &i.CaddieID, + &i.ClubID, + &i.BackgroundID, + &i.FrameID, + &i.StickerID, + &i.SlotID, + &i.CutInID, + &i.TitleID, + &i.Poster0ID, + &i.Poster1ID, + &i.CharacterID, + ) + return i, err +} + +const setPlayerCurrency = `-- name: SetPlayerCurrency :one +UPDATE player SET pang = ?, points = ? WHERE player_id = ? RETURNING pang, points +` + +type SetPlayerCurrencyParams struct { + Pang int64 + Points int64 + PlayerID int64 +} + +type SetPlayerCurrencyRow struct { + Pang int64 + Points int64 +} + +func (q *Queries) SetPlayerCurrency(ctx context.Context, arg SetPlayerCurrencyParams) (SetPlayerCurrencyRow, error) { + row := q.db.QueryRowContext(ctx, setPlayerCurrency, arg.Pang, arg.Points, arg.PlayerID) + var i SetPlayerCurrencyRow + err := row.Scan(&i.Pang, &i.Points) + return i, err +} + +const setPlayerDecoration = `-- name: SetPlayerDecoration :one +UPDATE player +SET + background_id = ?, + frame_id = ?, + sticker_id = ?, + slot_id = ?, + cut_in_id = ?, + title_id = ? +WHERE player_id = ? +RETURNING player_id, username, nickname, password_hash, pang, points, rank, ball_type_id, mascot_type_id, slot0_type_id, slot1_type_id, slot2_type_id, slot3_type_id, slot4_type_id, slot5_type_id, slot6_type_id, slot7_type_id, slot8_type_id, slot9_type_id, caddie_id, club_id, background_id, frame_id, sticker_id, slot_id, cut_in_id, title_id, poster0_id, poster1_id, character_id +` + +type SetPlayerDecorationParams struct { + BackgroundID sql.NullInt64 + FrameID sql.NullInt64 + StickerID sql.NullInt64 + SlotID sql.NullInt64 + CutInID sql.NullInt64 + TitleID sql.NullInt64 + PlayerID int64 +} + +func (q *Queries) SetPlayerDecoration(ctx context.Context, arg SetPlayerDecorationParams) (Player, error) { + row := q.db.QueryRowContext(ctx, setPlayerDecoration, + arg.BackgroundID, + arg.FrameID, + arg.StickerID, + arg.SlotID, + arg.CutInID, + arg.TitleID, + arg.PlayerID, + ) + var i Player + err := row.Scan( + &i.PlayerID, + &i.Username, + &i.Nickname, + &i.PasswordHash, + &i.Pang, + &i.Points, &i.Rank, + &i.BallTypeID, + &i.MascotTypeID, + &i.Slot0TypeID, + &i.Slot1TypeID, + &i.Slot2TypeID, + &i.Slot3TypeID, + &i.Slot4TypeID, + &i.Slot5TypeID, + &i.Slot6TypeID, + &i.Slot7TypeID, + &i.Slot8TypeID, + &i.Slot9TypeID, + &i.CaddieID, + &i.ClubID, + &i.BackgroundID, + &i.FrameID, + &i.StickerID, + &i.SlotID, + &i.CutInID, + &i.TitleID, + &i.Poster0ID, + &i.Poster1ID, + &i.CharacterID, ) return i, err } const setPlayerNickname = `-- name: SetPlayerNickname :one -UPDATE player SET nickname = ? WHERE player_id = ? RETURNING player_id, username, nickname, password_hash, pang, rank +UPDATE player SET nickname = ? WHERE player_id = ? RETURNING player_id, username, nickname, password_hash, pang, points, rank, ball_type_id, mascot_type_id, slot0_type_id, slot1_type_id, slot2_type_id, slot3_type_id, slot4_type_id, slot5_type_id, slot6_type_id, slot7_type_id, slot8_type_id, slot9_type_id, caddie_id, club_id, background_id, frame_id, sticker_id, slot_id, cut_in_id, title_id, poster0_id, poster1_id, character_id ` type SetPlayerNicknameParams struct { @@ -97,7 +795,31 @@ func (q *Queries) SetPlayerNickname(ctx context.Context, arg SetPlayerNicknamePa &i.Nickname, &i.PasswordHash, &i.Pang, + &i.Points, &i.Rank, + &i.BallTypeID, + &i.MascotTypeID, + &i.Slot0TypeID, + &i.Slot1TypeID, + &i.Slot2TypeID, + &i.Slot3TypeID, + &i.Slot4TypeID, + &i.Slot5TypeID, + &i.Slot6TypeID, + &i.Slot7TypeID, + &i.Slot8TypeID, + &i.Slot9TypeID, + &i.CaddieID, + &i.ClubID, + &i.BackgroundID, + &i.FrameID, + &i.StickerID, + &i.SlotID, + &i.CutInID, + &i.TitleID, + &i.Poster0ID, + &i.Poster1ID, + &i.CharacterID, ) return i, err } diff --git a/login/conn.go b/login/conn.go index b4855bd..a9cd811 100755 --- a/login/conn.go +++ b/login/conn.go @@ -26,21 +26,18 @@ import ( "github.com/pangbox/server/database/accounts" "github.com/pangbox/server/gen/dbmodels" "github.com/pangbox/server/gen/proto/go/topologypb" - "github.com/pangbox/server/gen/proto/go/topologypb/topologypbconnect" - "github.com/pangbox/server/pangya" ) // Conn holds the state for a connection to the server. type Conn struct { *common.ServerConn[ClientMessage, ServerMessage] - topologyClient topologypbconnect.TopologyServiceClient - accountsService *accounts.Service + s *Server } // GetServerList returns a server list using the topology store. func (c *Conn) GetServerList(ctx context.Context, typ topologypb.Server_Type) (*ServerList, error) { message := &ServerList{} - response, err := c.topologyClient.ListServers(ctx, connect.NewRequest(&topologypb.ListServersRequest{Type: typ})) + response, err := c.s.topologyClient.ListServers(ctx, connect.NewRequest(&topologypb.ListServersRequest{Type: typ})) if err != nil { return nil, fmt.Errorf("getting server list: %w", err) } @@ -86,7 +83,7 @@ func (c *Conn) Handle(ctx context.Context) error { var player dbmodels.Player switch t := msg.(type) { case *ClientLogin: - player, err = c.accountsService.Authenticate(ctx, t.Username.Value, t.Password.Value) + player, err = c.s.accountsService.Authenticate(ctx, t.Username.Value, t.Password.Value) default: return fmt.Errorf("expected ClientLogin, got %T", t) } @@ -127,7 +124,7 @@ func (c *Conn) Handle(ctx context.Context) error { Nickname: t.Nickname, }) case *ClientSetNickname: - player, err = c.accountsService.SetNickname(ctx, player.PlayerID, t.Nickname.Value) + player, err = c.s.accountsService.SetNickname(ctx, player.PlayerID, t.Nickname.Value) if err != nil { // TODO: need to handle error log.Errorf("Database error setting nickname: %v", err) @@ -140,7 +137,19 @@ func (c *Conn) Handle(ctx context.Context) error { } } - haveCharacters, err := c.accountsService.HasCharacters(ctx, player.PlayerID) + if !player.ClubID.Valid { + item, err := c.s.accountsService.AddClubSet(ctx, player.PlayerID, c.s.configProvider.GetDefaultClubSetTypeID()) + if err != nil { + return fmt.Errorf("creating default clubset: %w", err) + } + + err = c.s.accountsService.SetClubSet(ctx, player.PlayerID, item.ItemID) + if err != nil { + return fmt.Errorf("assigning default clubset: %w", err) + } + } + + haveCharacters, err := c.s.accountsService.HasCharacters(ctx, player.PlayerID) if err != nil { log.Errorf("Database error getting characters: %v", err) return nil @@ -161,10 +170,19 @@ func (c *Conn) Handle(ctx context.Context) error { switch t := msg.(type) { case *ClientSelectCharacter: - c.accountsService.AddCharacter(ctx, player.PlayerID, pangya.PlayerCharacterData{ - CharTypeID: t.CharacterID, - HairColor: t.HairColor, + defaults := c.s.configProvider.GetCharacterDefaults(uint8(t.CharacterID)) + dbchar, err := c.s.accountsService.AddCharacter(ctx, player.PlayerID, accounts.NewCharacterParams{ + CharTypeID: t.CharacterID, + HairColor: t.HairColor, + DefaultPartTypeIDs: defaults.DefaultPartTypeIDs, }) + if err != nil { + return fmt.Errorf("creating character: %w", err) + } + err = c.s.accountsService.SetCharacter(ctx, player.PlayerID, dbchar.CharacterID) + if err != nil { + return fmt.Errorf("setting new character: %w", err) + } c.SendMessage(ctx, &Server0011{}) break CharacterSetup default: @@ -173,7 +191,7 @@ func (c *Conn) Handle(ctx context.Context) error { } } - session, err := c.accountsService.AddSession(ctx, player.PlayerID, c.RemoteAddr().String()) + session, err := c.s.accountsService.AddSession(ctx, player.PlayerID, c.RemoteAddr().String()) if err != nil { log.Errorf("Error creating session in DB: %v", err) } diff --git a/login/server.go b/login/server.go index f450c7b..f601454 100755 --- a/login/server.go +++ b/login/server.go @@ -23,6 +23,7 @@ import ( "github.com/pangbox/server/common" "github.com/pangbox/server/database/accounts" + "github.com/pangbox/server/gameconfig" "github.com/pangbox/server/gen/proto/go/topologypb/topologypbconnect" log "github.com/sirupsen/logrus" ) @@ -31,6 +32,7 @@ import ( type Options struct { TopologyClient topologypbconnect.TopologyServiceClient AccountsService *accounts.Service + ConfigProvider gameconfig.Provider } // Server provides an implementation of the PangYa login server. @@ -38,6 +40,7 @@ type Server struct { topologyClient topologypbconnect.TopologyServiceClient accountsService *accounts.Service baseServer *common.BaseServer + configProvider gameconfig.Provider } // New creates a new instance of the login server. @@ -46,6 +49,7 @@ func New(opts Options) *Server { topologyClient: opts.TopologyClient, accountsService: opts.AccountsService, baseServer: &common.BaseServer{}, + configProvider: opts.ConfigProvider, } } @@ -60,8 +64,7 @@ func (s *Server) Listen(ctx context.Context, addr string) error { ClientMessageTable, ServerMessageTable, ), - topologyClient: s.topologyClient, - accountsService: s.accountsService, + s: s, } return conn.Handle(ctx) }) diff --git a/migrations/0001_create_player_table.sql b/migrations/0001_create_player_table.sql deleted file mode 100644 index 1f2479f..0000000 --- a/migrations/0001_create_player_table.sql +++ /dev/null @@ -1,12 +0,0 @@ --- +goose Up -CREATE TABLE player ( - player_id INTEGER PRIMARY KEY AUTOINCREMENT, - username TEXT NOT NULL UNIQUE, - nickname TEXT UNIQUE, - password_hash TEXT NOT NULL, - pang INTEGER NOT NULL DEFAULT 10000, - rank INTEGER NOT NULL DEFAULT 0 -); - --- +goose Down -DROP TABLE player; diff --git a/migrations/0001_initial.sql b/migrations/0001_initial.sql new file mode 100644 index 0000000..e9db784 --- /dev/null +++ b/migrations/0001_initial.sql @@ -0,0 +1,130 @@ +-- +goose Up +CREATE TABLE player ( + player_id INTEGER PRIMARY KEY AUTOINCREMENT, + username TEXT NOT NULL UNIQUE, + nickname TEXT UNIQUE, + password_hash TEXT NOT NULL, + pang INTEGER NOT NULL DEFAULT 10000, + points INTEGER NOT NULL DEFAULT 0, + rank INTEGER NOT NULL DEFAULT 0, + ball_type_id INTEGER NOT NULL DEFAULT 0, + mascot_type_id INTEGER NOT NULL DEFAULT 0, + slot0_type_id INTEGER NOT NULL DEFAULT 0, + slot1_type_id INTEGER NOT NULL DEFAULT 0, + slot2_type_id INTEGER NOT NULL DEFAULT 0, + slot3_type_id INTEGER NOT NULL DEFAULT 0, + slot4_type_id INTEGER NOT NULL DEFAULT 0, + slot5_type_id INTEGER NOT NULL DEFAULT 0, + slot6_type_id INTEGER NOT NULL DEFAULT 0, + slot7_type_id INTEGER NOT NULL DEFAULT 0, + slot8_type_id INTEGER NOT NULL DEFAULT 0, + slot9_type_id INTEGER NOT NULL DEFAULT 0 +); + +CREATE TABLE inventory ( + item_id INTEGER PRIMARY KEY AUTOINCREMENT, + player_id INTEGER REFERENCES player(player_id) ON DELETE CASCADE NOT NULL, + item_type_id INTEGER NOT NULL, + quantity INTEGER +); + +ALTER TABLE player ADD COLUMN caddie_id INTEGER REFERENCES inventory(item_id) ON DELETE CASCADE; +ALTER TABLE player ADD COLUMN club_id INTEGER REFERENCES inventory(item_id) ON DELETE CASCADE; +ALTER TABLE player ADD COLUMN background_id INTEGER REFERENCES inventory(item_id) ON DELETE CASCADE; +ALTER TABLE player ADD COLUMN frame_id INTEGER REFERENCES inventory(item_id) ON DELETE CASCADE; +ALTER TABLE player ADD COLUMN sticker_id INTEGER REFERENCES inventory(item_id) ON DELETE CASCADE; +ALTER TABLE player ADD COLUMN slot_id INTEGER REFERENCES inventory(item_id) ON DELETE CASCADE; +ALTER TABLE player ADD COLUMN cut_in_id INTEGER REFERENCES inventory(item_id) ON DELETE CASCADE; +ALTER TABLE player ADD COLUMN title_id INTEGER REFERENCES inventory(item_id) ON DELETE CASCADE; +ALTER TABLE player ADD COLUMN poster0_id INTEGER REFERENCES inventory(item_id) ON DELETE CASCADE; +ALTER TABLE player ADD COLUMN poster1_id INTEGER REFERENCES inventory(item_id) ON DELETE CASCADE; + +CREATE TABLE character ( + character_id INTEGER PRIMARY KEY AUTOINCREMENT, + player_id INTEGER REFERENCES player(player_id) ON DELETE CASCADE NOT NULL, + item_id INTEGER REFERENCES inventory(item_id) ON DELETE CASCADE NOT NULL, + hair_color INTEGER NOT NULL, + shirt INTEGER NOT NULL, + mastery INTEGER NOT NULL, + + -- Part IDs for equipped clothing/etc. + part00_item_id INTEGER REFERENCES inventory(item_id) ON DELETE CASCADE, + part01_item_id INTEGER REFERENCES inventory(item_id) ON DELETE CASCADE, + part02_item_id INTEGER REFERENCES inventory(item_id) ON DELETE CASCADE, + part03_item_id INTEGER REFERENCES inventory(item_id) ON DELETE CASCADE, + part04_item_id INTEGER REFERENCES inventory(item_id) ON DELETE CASCADE, + part05_item_id INTEGER REFERENCES inventory(item_id) ON DELETE CASCADE, + part06_item_id INTEGER REFERENCES inventory(item_id) ON DELETE CASCADE, + part07_item_id INTEGER REFERENCES inventory(item_id) ON DELETE CASCADE, + part08_item_id INTEGER REFERENCES inventory(item_id) ON DELETE CASCADE, + part09_item_id INTEGER REFERENCES inventory(item_id) ON DELETE CASCADE, + part10_item_id INTEGER REFERENCES inventory(item_id) ON DELETE CASCADE, + part11_item_id INTEGER REFERENCES inventory(item_id) ON DELETE CASCADE, + part12_item_id INTEGER REFERENCES inventory(item_id) ON DELETE CASCADE, + part13_item_id INTEGER REFERENCES inventory(item_id) ON DELETE CASCADE, + part14_item_id INTEGER REFERENCES inventory(item_id) ON DELETE CASCADE, + part15_item_id INTEGER REFERENCES inventory(item_id) ON DELETE CASCADE, + part16_item_id INTEGER REFERENCES inventory(item_id) ON DELETE CASCADE, + part17_item_id INTEGER REFERENCES inventory(item_id) ON DELETE CASCADE, + part18_item_id INTEGER REFERENCES inventory(item_id) ON DELETE CASCADE, + part19_item_id INTEGER REFERENCES inventory(item_id) ON DELETE CASCADE, + part20_item_id INTEGER REFERENCES inventory(item_id) ON DELETE CASCADE, + part21_item_id INTEGER REFERENCES inventory(item_id) ON DELETE CASCADE, + part22_item_id INTEGER REFERENCES inventory(item_id) ON DELETE CASCADE, + part23_item_id INTEGER REFERENCES inventory(item_id) ON DELETE CASCADE, + + -- Part IFF IDs for equipped clothing/etc. + -- n.b.: This may be non-zero even though there is no underlying item. + part00_item_type_id INTEGER NOT NULL DEFAULT 0, + part01_item_type_id INTEGER NOT NULL DEFAULT 0, + part02_item_type_id INTEGER NOT NULL DEFAULT 0, + part03_item_type_id INTEGER NOT NULL DEFAULT 0, + part04_item_type_id INTEGER NOT NULL DEFAULT 0, + part05_item_type_id INTEGER NOT NULL DEFAULT 0, + part06_item_type_id INTEGER NOT NULL DEFAULT 0, + part07_item_type_id INTEGER NOT NULL DEFAULT 0, + part08_item_type_id INTEGER NOT NULL DEFAULT 0, + part09_item_type_id INTEGER NOT NULL DEFAULT 0, + part10_item_type_id INTEGER NOT NULL DEFAULT 0, + part11_item_type_id INTEGER NOT NULL DEFAULT 0, + part12_item_type_id INTEGER NOT NULL DEFAULT 0, + part13_item_type_id INTEGER NOT NULL DEFAULT 0, + part14_item_type_id INTEGER NOT NULL DEFAULT 0, + part15_item_type_id INTEGER NOT NULL DEFAULT 0, + part16_item_type_id INTEGER NOT NULL DEFAULT 0, + part17_item_type_id INTEGER NOT NULL DEFAULT 0, + part18_item_type_id INTEGER NOT NULL DEFAULT 0, + part19_item_type_id INTEGER NOT NULL DEFAULT 0, + part20_item_type_id INTEGER NOT NULL DEFAULT 0, + part21_item_type_id INTEGER NOT NULL DEFAULT 0, + part22_item_type_id INTEGER NOT NULL DEFAULT 0, + part23_item_type_id INTEGER NOT NULL DEFAULT 0, + + aux_part0_id INTEGER REFERENCES inventory(item_id) ON DELETE CASCADE, + aux_part1_id INTEGER REFERENCES inventory(item_id) ON DELETE CASCADE, + aux_part2_id INTEGER REFERENCES inventory(item_id) ON DELETE CASCADE, + aux_part3_id INTEGER REFERENCES inventory(item_id) ON DELETE CASCADE, + aux_part4_id INTEGER REFERENCES inventory(item_id) ON DELETE CASCADE, + + cut_in_id INTEGER REFERENCES inventory(item_id) ON DELETE CASCADE +); + +ALTER TABLE player ADD COLUMN character_id INTEGER REFERENCES character(character_id) ON DELETE CASCADE; + +CREATE TABLE session ( + session_id INTEGER PRIMARY KEY AUTOINCREMENT, + player_id INTEGER REFERENCES player(player_id) ON DELETE CASCADE NOT NULL, + session_key TEXT NOT NULL UNIQUE, + session_address TEXT NOT NULL, + session_expires_at INTEGER NOT NULL +); + +CREATE UNIQUE INDEX `session_key_idx` ON session (session_key); + +-- +goose Down +DROP INDEX session_key_idx; +DROP TABLE session; +DROP TABLE character; +DROP TABLE inventory; +DROP TABLE player; + diff --git a/migrations/0002_create_character_table.sql b/migrations/0002_create_character_table.sql deleted file mode 100644 index 92fde23..0000000 --- a/migrations/0002_create_character_table.sql +++ /dev/null @@ -1,10 +0,0 @@ --- +goose Up -CREATE TABLE character ( - character_id INTEGER PRIMARY KEY AUTOINCREMENT, - player_id INTEGER REFERENCES player(player_id) ON DELETE CASCADE NOT NULL, - character_type_id INTEGER NOT NULL, - character_data BLOB NOT NULL -); - --- +goose Down -DROP TABLE character; diff --git a/migrations/0003_create_sessions_table.sql b/migrations/0003_create_sessions_table.sql deleted file mode 100644 index ff51c46..0000000 --- a/migrations/0003_create_sessions_table.sql +++ /dev/null @@ -1,15 +0,0 @@ --- +goose Up -CREATE TABLE session ( - session_id INTEGER PRIMARY KEY AUTOINCREMENT, - player_id INTEGER REFERENCES player(player_id) ON DELETE CASCADE NOT NULL, - session_key TEXT NOT NULL UNIQUE, - session_address TEXT NOT NULL, - session_expires_at INTEGER NOT NULL -); - -CREATE UNIQUE INDEX session_key_idx ON session (session_key); - --- +goose Down -DROP INDEX session_key_idx; - -DROP TABLE session; diff --git a/minibox/gameserver.go b/minibox/gameserver.go index 5deb92e..84d62e2 100644 --- a/minibox/gameserver.go +++ b/minibox/gameserver.go @@ -22,6 +22,7 @@ import ( "github.com/pangbox/server/database/accounts" gameserver "github.com/pangbox/server/game/server" + "github.com/pangbox/server/gameconfig" "github.com/pangbox/server/gen/proto/go/topologypb/topologypbconnect" "github.com/pangbox/server/pangya/iff" log "github.com/sirupsen/logrus" @@ -54,6 +55,7 @@ func (g *GameServer) Configure(opts GameOptions) error { PangyaIFF: opts.PangyaIFF, ServerID: opts.ServerID, ChannelName: opts.ChannelName, + ConfigProvider: gameconfig.Default(), }) service.SetShutdownFunc(func(shutdownCtx context.Context) error { diff --git a/minibox/loginserver.go b/minibox/loginserver.go index 571dd63..1517db2 100644 --- a/minibox/loginserver.go +++ b/minibox/loginserver.go @@ -21,6 +21,7 @@ import ( "context" "github.com/pangbox/server/database/accounts" + "github.com/pangbox/server/gameconfig" "github.com/pangbox/server/gen/proto/go/topologypb/topologypbconnect" "github.com/pangbox/server/login" log "github.com/sirupsen/logrus" @@ -47,6 +48,7 @@ func (l *LoginServer) Configure(opts LoginOptions) error { loginServer := login.New(login.Options{ TopologyClient: opts.TopologyClient, AccountsService: opts.AccountsService, + ConfigProvider: gameconfig.Default(), }) service.SetShutdownFunc(func(shutdownCtx context.Context) error { diff --git a/pangya/player.go b/pangya/player.go index 27e17d5..2ecb607 100755 --- a/pangya/player.go +++ b/pangya/player.go @@ -104,12 +104,12 @@ type PlayerEquipment struct { Items PlayerEquippedItems - BackgroundID uint32 - FrameID uint32 - StickerID uint32 - SlotID uint32 - SlotCutInID uint32 - SlotRankBannerID uint32 + BackgroundID uint32 + FrameID uint32 + StickerID uint32 + SlotID uint32 + CutInID uint32 + TitleID uint32 BackgroundTypeID uint32 FrameTypeID uint32 @@ -160,7 +160,6 @@ type PlayerCharacterData struct { CutInID uint32 Unknown4 [16]byte Stats [5]byte - Mastery int CardChar [4]uint32 CardCaddie [4]uint32 CardNPC [4]uint32 @@ -197,7 +196,7 @@ type PlayerData struct { UserInfo PlayerInfo PlayerStats PlayerStats Trophy [13][3]uint16 - Items PlayerEquipment + EquippedItems PlayerEquipment SeasonHistory PlayerSeasonHistory EquippedCharacter PlayerCharacterData EquippedCaddie PlayerCaddieData diff --git a/queries/character.sql b/queries/character.sql index 3c13a8e..90c7db6 100644 --- a/queries/character.sql +++ b/queries/character.sql @@ -1,21 +1,148 @@ -- name: GetCharacter :one -SELECT * FROM character -WHERE character_id = ? LIMIT 1; +SELECT + character.*, + inventory_character.item_type_id AS character_type_id, + inventory_aux_part0.item_type_id AS inventory_aux_part0_type_id_FIXNULL, + inventory_aux_part1.item_type_id AS inventory_aux_part1_type_id_FIXNULL, + inventory_aux_part2.item_type_id AS inventory_aux_part2_type_id_FIXNULL, + inventory_aux_part3.item_type_id AS inventory_aux_part3_type_id_FIXNULL, + inventory_aux_part4.item_type_id AS inventory_aux_part4_type_id_FIXNULL +FROM character AS character +LEFT JOIN inventory AS inventory_character ON (character.item_id = inventory_character.item_id) +LEFT JOIN inventory AS inventory_aux_part0 ON (character.aux_part0_id = inventory_aux_part0.item_id) +LEFT JOIN inventory AS inventory_aux_part1 ON (character.aux_part1_id = inventory_aux_part1.item_id) +LEFT JOIN inventory AS inventory_aux_part2 ON (character.aux_part2_id = inventory_aux_part2.item_id) +LEFT JOIN inventory AS inventory_aux_part3 ON (character.aux_part3_id = inventory_aux_part3.item_id) +LEFT JOIN inventory AS inventory_aux_part4 ON (character.aux_part4_id = inventory_aux_part4.item_id) +WHERE character.character_id = ? LIMIT 1; -- name: GetCharactersByPlayer :many -SELECT * FROM character -WHERE player_id = ?; +SELECT + character.*, + inventory_character.item_type_id AS character_type_id +FROM character AS character +LEFT JOIN inventory AS inventory_character ON (character.item_id = inventory_character.item_id) +WHERE character.player_id = ?; -- name: PlayerHasCharacters :one SELECT count(*) > 0 FROM character -WHERE player_id = ?; +WHERE character.player_id = ?; -- name: CreateCharacter :one INSERT INTO character ( player_id, - character_type_id, - character_data + item_id, + hair_color, + shirt, + mastery, + part00_item_type_id, + part01_item_type_id, + part02_item_type_id, + part03_item_type_id, + part04_item_type_id, + part05_item_type_id, + part06_item_type_id, + part07_item_type_id, + part08_item_type_id, + part09_item_type_id, + part10_item_type_id, + part11_item_type_id, + part12_item_type_id, + part13_item_type_id, + part14_item_type_id, + part15_item_type_id, + part16_item_type_id, + part17_item_type_id, + part18_item_type_id, + part19_item_type_id, + part20_item_type_id, + part21_item_type_id, + part22_item_type_id, + part23_item_type_id ) VALUES ( - ?, ?, ? + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ? ) RETURNING *; + +-- name: SetCharacterParts :one +UPDATE character +SET + part00_item_id = ?, + part01_item_id = ?, + part02_item_id = ?, + part03_item_id = ?, + part04_item_id = ?, + part05_item_id = ?, + part06_item_id = ?, + part07_item_id = ?, + part08_item_id = ?, + part09_item_id = ?, + part10_item_id = ?, + part11_item_id = ?, + part12_item_id = ?, + part13_item_id = ?, + part14_item_id = ?, + part15_item_id = ?, + part16_item_id = ?, + part17_item_id = ?, + part18_item_id = ?, + part19_item_id = ?, + part20_item_id = ?, + part21_item_id = ?, + part22_item_id = ?, + part23_item_id = ?, + part00_item_type_id = ?, + part01_item_type_id = ?, + part02_item_type_id = ?, + part03_item_type_id = ?, + part04_item_type_id = ?, + part05_item_type_id = ?, + part06_item_type_id = ?, + part07_item_type_id = ?, + part08_item_type_id = ?, + part09_item_type_id = ?, + part10_item_type_id = ?, + part11_item_type_id = ?, + part12_item_type_id = ?, + part13_item_type_id = ?, + part14_item_type_id = ?, + part15_item_type_id = ?, + part16_item_type_id = ?, + part17_item_type_id = ?, + part18_item_type_id = ?, + part19_item_type_id = ?, + part20_item_type_id = ?, + part21_item_type_id = ?, + part22_item_type_id = ?, + part23_item_type_id = ?, + cut_in_id = ? +WHERE character_id = ? +RETURNING *; diff --git a/queries/inventory.sql b/queries/inventory.sql new file mode 100644 index 0000000..d626e93 --- /dev/null +++ b/queries/inventory.sql @@ -0,0 +1,26 @@ +-- name: AddItemToInventory :one +INSERT INTO inventory ( + player_id, + item_type_id, + quantity +) VALUES ( + ?, + ?, + ? +) +RETURNING *; + +-- name: RemoveItemFromInventory :exec +DELETE FROM inventory WHERE player_id = ? AND item_id = ?; + +-- name: SetItemQuantity :one +UPDATE inventory SET quantity = ? WHERE player_id = ? AND item_id = ? RETURNING *; + +-- name: GetPlayerInventory :many +SELECT * FROM inventory WHERE player_id = ?; + +-- name: GetItemsByTypeID :many +SELECT * FROM inventory WHERE player_id = ? AND item_type_id = ?; + +-- name: GetItem :one +SELECT * FROM inventory WHERE player_id = ? AND item_id = ?; diff --git a/queries/player.sql b/queries/player.sql index 723fb7f..b06da28 100644 --- a/queries/player.sql +++ b/queries/player.sql @@ -1,10 +1,38 @@ -- name: GetPlayer :one -SELECT * FROM player -WHERE player_id = ? LIMIT 1; +SELECT + player.*, + character.*, + inventory_character.item_type_id AS character_type_id_FIXNULL, + inventory_caddie.item_type_id AS caddie_type_id_FIXNULL, + inventory_club.item_type_id AS club_type_id_FIXNULL, + inventory_background.item_type_id AS background_type_id_FIXNULL, + inventory_frame.item_type_id AS frame_type_id_FIXNULL, + inventory_sticker.item_type_id AS sticker_type_id_FIXNULL, + inventory_slot.item_type_id AS slot_type_id_FIXNULL, + inventory_cut_in.item_type_id AS cut_in_type_id_FIXNULL, + inventory_title.item_type_id AS title_type_id_FIXNULL, + inventory_poster0.item_type_id AS poster0_type_id_FIXNULL, + inventory_poster1.item_type_id AS poster1_type_id_FIXNULL +FROM player AS player +LEFT JOIN character USING (character_id) +LEFT JOIN inventory AS inventory_character ON (character.item_id = inventory_character.item_id) +LEFT JOIN inventory AS inventory_caddie ON (player.caddie_id = inventory_caddie.item_id) +LEFT JOIN inventory AS inventory_club ON (player.club_id = inventory_club.item_id) +LEFT JOIN inventory AS inventory_background ON (player.background_id = inventory_background.item_id) +LEFT JOIN inventory AS inventory_frame ON (player.frame_id = inventory_frame.item_id) +LEFT JOIN inventory AS inventory_sticker ON (player.sticker_id = inventory_sticker.item_id) +LEFT JOIN inventory AS inventory_slot ON (player.slot_id = inventory_slot.item_id) +LEFT JOIN inventory AS inventory_cut_in ON (player.cut_in_id = inventory_cut_in.item_id) +LEFT JOIN inventory AS inventory_title ON (player.title_id = inventory_title.item_id) +LEFT JOIN inventory AS inventory_poster0 ON (player.poster0_id = inventory_poster0.item_id) +LEFT JOIN inventory AS inventory_poster1 ON (player.poster1_id = inventory_poster1.item_id) +WHERE player.player_id = ? +LIMIT 1; -- name: GetPlayerByUsername :one SELECT * FROM player -WHERE username = ? LIMIT 1; +WHERE username = ? +LIMIT 1; -- name: CreatePlayer :one INSERT INTO player ( @@ -18,3 +46,64 @@ RETURNING *; -- name: SetPlayerNickname :one UPDATE player SET nickname = ? WHERE player_id = ? RETURNING *; + +-- name: SetPlayerCharacter :one +UPDATE player SET character_id = ? WHERE player_id = ? RETURNING *; + +-- name: SetPlayerClubSet :one +UPDATE player SET club_id = ? WHERE player_id = ? RETURNING *; + +-- name: SetPlayerCaddie :one +UPDATE player SET caddie_id = ? WHERE player_id = ? RETURNING *; + +-- name: GetPlayerConsumables :one +SELECT + slot0_type_id, + slot1_type_id, + slot2_type_id, + slot3_type_id, + slot4_type_id, + slot5_type_id, + slot6_type_id, + slot7_type_id, + slot8_type_id, + slot9_type_id +FROM player +WHERE player_id = ?; + +-- name: SetPlayerConsumables :one +UPDATE player +SET + slot0_type_id = ?, + slot1_type_id = ?, + slot2_type_id = ?, + slot3_type_id = ?, + slot4_type_id = ?, + slot5_type_id = ?, + slot6_type_id = ?, + slot7_type_id = ?, + slot8_type_id = ?, + slot9_type_id = ? +WHERE player_id = ? +RETURNING *; + +-- name: SetPlayerComet :one +UPDATE player SET ball_type_id = ? WHERE player_id = ? RETURNING *; + +-- name: SetPlayerDecoration :one +UPDATE player +SET + background_id = ?, + frame_id = ?, + sticker_id = ?, + slot_id = ?, + cut_in_id = ?, + title_id = ? +WHERE player_id = ? +RETURNING *; + +-- name: GetPlayerCurrency :one +SELECT pang, points FROM player WHERE player_id = ?; + +-- name: SetPlayerCurrency :one +UPDATE player SET pang = ?, points = ? WHERE player_id = ? RETURNING pang, points; \ No newline at end of file diff --git a/sqlc.sh b/sqlc.sh new file mode 100755 index 0000000..8238c9d --- /dev/null +++ b/sqlc.sh @@ -0,0 +1,5 @@ +#!/bin/sh +go run github.com/kyleconroy/sqlc/cmd/sqlc generate --no-remote + +# Workaround for LEFT JOIN nullability issues in sqlc. +sed -i -E 's/FIXNULL([[:space:]]+)int64/ \1sql.NullInt64/g; s/FIXNULL//g' gen/dbmodels/player.sql.go