diff --git a/db/inventory.go b/db/inventory.go index a3c7829..bda80e5 100644 --- a/db/inventory.go +++ b/db/inventory.go @@ -1,8 +1,9 @@ package db import ( + "database/sql" + "github.com/CPunch/GopenFusion/protocol" - "github.com/blockloop/scan" ) type Inventory struct { @@ -16,14 +17,21 @@ type Inventory struct { // start && end are both inclusive func (db *DBHandler) GetPlayerInventorySlots(PlayerID int, start int, end int) ([]protocol.SItemBase, error) { - rows, err := db.Query("SELECT * FROM Inventory WHERE Slot BETWEEN ? AND ? AND PlayerID = ?", start, end, PlayerID) + rows, err := db.Query("SELECT PlayerID, Slot, ID, Type, Opt, TimeLimit FROM Inventory WHERE Slot BETWEEN ? AND ? AND PlayerID = ?", start, end, PlayerID) if err != nil { return nil, err } var inven []Inventory - if err := scan.Row(&inven, rows); err != nil { - return make([]protocol.SItemBase, end-start), nil + for rows.Next() { + item := Inventory{} + + if err := rows.Scan( + &item.PlayerID, &item.Slot, &item.ID, &item.Type, &item.Opt, &item.TimeLimit); err != nil { + return nil, err + } + + inven = append(inven, item) } // populate an SItemBase array @@ -39,23 +47,25 @@ func (db *DBHandler) GetPlayerInventorySlots(PlayerID int, start int, end int) ( return items, nil } -// start is inclusive TODO: needs to be a transaction !! +// start is inclusive func (db *DBHandler) SetPlayerInventorySlots(PlayerID int, start int, items []protocol.SItemBase) error { - // delete inventory slots - _, err := db.Query("DELETE FROM Inventory WHERE Slot BETWEEN ? AND ? AND PlayerID = ?", start, start+len(items)-1, PlayerID) - if err != nil { - return err - } + return db.Transaction(func(tx *sql.Tx) error { + // delete inventory slots + _, err := db.Query("DELETE FROM Inventory WHERE Slot BETWEEN ? AND ? AND PlayerID = ?", start, start+len(items)-1, PlayerID) + if err != nil { + return err + } - // insert inventory - for i, item := range items { - if item.IID != 0 { - _, err := db.Query("INSERT INTO Inventory (PlayerID, Slot, ID, Type, Opt, TimeLimit) VALUES (?, ?, ?, ?, ?, ?)", PlayerID, start+i, item.IID, item.IType, item.IOpt, item.ITimeLimit) - if err != nil { - return err + // insert inventory + for i, item := range items { + if item.IID != 0 { + _, err := db.Query("INSERT INTO Inventory (PlayerID, Slot, ID, Type, Opt, TimeLimit) VALUES (?, ?, ?, ?, ?, ?)", PlayerID, start+i, item.IID, item.IType, item.IOpt, item.ITimeLimit) + if err != nil { + return err + } } } - } - return nil + return nil + }) } diff --git a/db/players.go b/db/players.go index 613a2b0..04b2eeb 100644 --- a/db/players.go +++ b/db/players.go @@ -2,7 +2,6 @@ package db import ( "database/sql" - "fmt" "github.com/CPunch/GopenFusion/config" "github.com/CPunch/GopenFusion/protocol" @@ -112,7 +111,7 @@ func (db *DBHandler) FinishPlayer(character *protocol.SP_CL2LS_REQ_CHAR_CREATE, // update Inventory items := [3]int16{character.SOn_Item.IEquipUBID, character.SOn_Item.IEquipLBID, character.SOn_Item.IEquipFootID} for i := 0; i < len(items); i++ { - _, err = tx.Exec("INSERT INTO Inventory (PlayerID, Slot, ID, Type, Opt) VALUES (?, ?, ?, ?, 1)", character.PCStyle.IPC_UID, i+1, items[i], i+1) + _, err = tx.Exec("INSERT INTO Inventory (PlayerID, Slot, ID, Type, Opt) VALUES (?, ?, ?, ?, 1)", character.PCStyle.IPC_UID, i, items[i], i+1) if err != nil { return err } @@ -132,19 +131,21 @@ func (db *DBHandler) FinishTutorial(PlayerID, AccountID int) error { return nil } -// returns the deleted player row -func (db *DBHandler) DeletePlayer(PlayerID, AccountID int) (*Player, error) { - row, err := db.Query("DELETE FROM Players WHERE AccountID = ? AND PlayerID = ? RETURNING *") +// returns the deleted Slot number +func (db *DBHandler) DeletePlayer(PlayerID, AccountID int) (int, error) { + row, err := db.Query("DELETE FROM Players WHERE AccountID = ? AND PlayerID = ? RETURNING Slot") if err != nil { - return nil, err + return -1, err } - var plr Player - if err := scan.Row(&plr, row); err != nil { - return nil, err + var slot int + for row.Next() { + if err := row.Scan(&slot); err != nil { + return -1, err + } } - return &plr, nil + return slot, nil } func (db *DBHandler) GetPlayer(PlayerID int) (*Player, error) { @@ -165,7 +166,6 @@ func (db *DBHandler) GetPlayer(PlayerID int) (*Player, error) { return nil, err } - fmt.Print(PlayerID) var plr Player for rows.Next() { if err := rows.Scan( diff --git a/protocol/packet.go b/protocol/packet.go index 0d7d342..8634b16 100644 --- a/protocol/packet.go +++ b/protocol/packet.go @@ -20,8 +20,6 @@ type Packet struct { cursor int // to keep track of things like member alignment for easier debugging } -const PACK_ALIGN = 4 - func NewPacket(buf []byte) *Packet { pkt := &Packet{ ByteOrder: binary.LittleEndian, diff --git a/server/login.go b/server/login.go index 73eda2b..29bcfc3 100644 --- a/server/login.go +++ b/server/login.go @@ -124,6 +124,17 @@ func (server *LoginServer) Login(peer *Peer, pkt *protocol.Packet) { server.AcceptLogin(peer, loginPkt.SzID, loginPkt.IClientVerC, 1, charInfo) } +func (server *LoginServer) CheckCharacterName(peer *Peer, pkt *protocol.Packet) { + var charPkt protocol.SP_CL2LS_REQ_CHECK_CHAR_NAME + pkt.Decode(&charPkt) + + // just auto accept, client resends this data later + peer.Send(&protocol.SP_LS2CL_REP_CHECK_CHAR_NAME_SUCC{ + SzFirstName: charPkt.SzFirstName, + SzLastName: charPkt.SzLastName, + }, protocol.P_LS2CL_REP_CHECK_CHAR_NAME_SUCC) +} + func (server *LoginServer) SaveCharacterName(peer *Peer, pkt *protocol.Packet) { var charPkt protocol.SP_CL2LS_REQ_SAVE_CHAR_NAME pkt.Decode(&charPkt) @@ -133,6 +144,7 @@ func (server *LoginServer) SaveCharacterName(peer *Peer, pkt *protocol.Packet) { panic(fmt.Errorf("Out of order P_LS2CL_REP_SAVE_CHAR_NAME_FAIL!")) } + // TODO: sanity check SzFirstName && SzLastName PlayerID, err := db.DefaultDB.NewPlayer(peer.AccountID, charPkt.SzFirstName, charPkt.SzLastName, int(charPkt.ISlotNum)) if err != nil { peer.Send(&protocol.SP_LS2CL_REP_SAVE_CHAR_NAME_FAIL{}, protocol.P_LS2CL_REP_SAVE_CHAR_NAME_FAIL) @@ -199,6 +211,33 @@ func (server *LoginServer) CharacterCreate(peer *Peer, pkt *protocol.Packet) { ILevel: int16(plr.Level), SPC_Style: PCStyle, SPC_Style2: PCStyle2, - SOn_Item: protocol.SOnItem{ /*TODO*/ }, + SOn_Item: charPkt.SOn_Item, // if the items were faked, we don't really care since the db only stores the sanitized fields }, protocol.P_LS2CL_REP_CHAR_CREATE_SUCC) } + +func (server *LoginServer) CharacterDelete(peer *Peer, pkt *protocol.Packet) { + var charPkt protocol.SP_CL2LS_REQ_CHAR_DELETE + pkt.Decode(&charPkt) + + slot, err := db.DefaultDB.DeletePlayer(int(charPkt.IPC_UID), peer.AccountID) + if err != nil { + peer.Send(&protocol.SP_LS2CL_REP_SHARD_SELECT_FAIL{IErrorCode: 2}, protocol.P_LS2CL_REP_SHARD_SELECT_FAIL) + panic(err) + } + + peer.Send(&protocol.SP_LS2CL_REP_CHAR_DELETE_SUCC{ + ISlotNum: int8(slot), + }, protocol.P_LS2CL_REP_CHAR_DELETE_SUCC) +} + +func (server *LoginServer) FinishTutorial(peer *Peer, pkt *protocol.Packet) { + var charPkt protocol.SP_CL2LS_REQ_SAVE_CHAR_TUTOR + pkt.Decode(&charPkt) + + if err := db.DefaultDB.FinishTutorial(int(charPkt.IPC_UID), peer.AccountID); err != nil { + peer.Send(&protocol.SP_LS2CL_REP_SHARD_SELECT_FAIL{IErrorCode: 2}, protocol.P_LS2CL_REP_SHARD_SELECT_FAIL) + panic(err) + } + + // no response +} diff --git a/server/loginserver.go b/server/loginserver.go index 12010ba..fed6184 100644 --- a/server/loginserver.go +++ b/server/loginserver.go @@ -47,17 +47,31 @@ func (server *LoginServer) HandlePacket(peer *Peer, typeID uint32, pkt *protocol case protocol.P_CL2LS_REQ_LOGIN: server.Login(peer, pkt) case protocol.P_CL2LS_REQ_CHECK_CHAR_NAME: - var charPkt protocol.SP_CL2LS_REQ_CHECK_CHAR_NAME - pkt.Decode(&charPkt) - - peer.Send(&protocol.SP_LS2CL_REP_CHECK_CHAR_NAME_SUCC{ - SzFirstName: charPkt.SzFirstName, - SzLastName: charPkt.SzLastName, - }, protocol.P_LS2CL_REP_CHECK_CHAR_NAME_SUCC) + server.CheckCharacterName(peer, pkt) case protocol.P_CL2LS_REQ_SAVE_CHAR_NAME: server.SaveCharacterName(peer, pkt) case protocol.P_CL2LS_REQ_CHAR_CREATE: server.CharacterCreate(peer, pkt) + case protocol.P_CL2LS_REQ_CHAR_SELECT: + /* stubbed */ + case protocol.P_CL2LS_REQ_CHAR_DELETE: + server.CharacterDelete(peer, pkt) + case protocol.P_CL2LS_REQ_SHARD_SELECT: + /* stubbed */ + case protocol.P_CL2LS_REQ_SHARD_LIST_INFO: + /* stubbed */ + case protocol.P_CL2LS_CHECK_NAME_LIST: + /* stubbed */ + case protocol.P_CL2LS_REQ_SAVE_CHAR_TUTOR: + server.FinishTutorial(peer, pkt) + case protocol.P_CL2LS_REQ_PC_EXIT_DUPLICATE: + /* stubbed */ + case protocol.P_CL2LS_REP_LIVE_CHECK: + /* stubbed */ + case protocol.P_CL2LS_REQ_CHANGE_CHAR_NAME: + /* stubbed */ + case protocol.P_CL2LS_REQ_SERVER_SELECT: + /* stubbed */ default: log.Printf("[WARN] unsupported packet ID: %x\n", typeID) }