mirror of
https://github.com/CPunch/gopenfusion.git
synced 2024-11-14 12:00:05 +00:00
CPunch
1f66acfd25
started out as me making a service abstraction.. - db.Player exists again, and entity.Player uses it as an embedded struct - chunk.ForEachEntity() lets you add/remove entities during iteration now - removed account related fields from CNPeer - protocol/pool has been merged with protocol. use protocol.GetBuffer() and protocol.PutBuffer(). - new protocol/internal/service! service.Service is an abstraction layer to handle multiple CNPeer* connections and allows you to associate each with an interface{} uData. In the future it might also handle a task queue for jobs that modify/interact with the player's uData, called from service.handleEvents() - PacketHandler callback type has a new param! uData is passed as well now - much of loginserver/shardserver is now handled by the shared service abstraction - SHARD: NPC_ENTER packets are now sent on player loading complete rather than on enter.
230 lines
6.9 KiB
Go
230 lines
6.9 KiB
Go
package db
|
|
|
|
import (
|
|
"database/sql"
|
|
|
|
"github.com/CPunch/gopenfusion/config"
|
|
"github.com/CPunch/gopenfusion/internal/protocol"
|
|
"github.com/blockloop/scan"
|
|
)
|
|
|
|
type Player struct {
|
|
PlayerID int
|
|
AccountID int
|
|
AccountLevel int
|
|
Slot int
|
|
PCStyle protocol.SPCStyle
|
|
PCStyle2 protocol.SPCStyle2
|
|
EquippedNanos [3]int
|
|
Nanos [config.NANO_COUNT]protocol.SNano
|
|
Equip [config.AEQUIP_COUNT]protocol.SItemBase
|
|
Inven [config.AINVEN_COUNT]protocol.SItemBase
|
|
Bank [config.ABANK_COUNT]protocol.SItemBase
|
|
SkywayLocationFlag []byte
|
|
FirstUseFlag []byte
|
|
Quests []byte
|
|
HP int
|
|
Level int
|
|
Taros int
|
|
FusionMatter int
|
|
Mentor int
|
|
X, Y, Z int
|
|
Angle int
|
|
BatteryN int
|
|
BatteryW int
|
|
WarpLocationFlag int
|
|
ActiveNanoSlotNum int
|
|
Fatigue int
|
|
CurrentMissionID int
|
|
IPCState int8
|
|
}
|
|
|
|
// returns PlayerID, error
|
|
func (db *DBHandler) NewPlayer(AccountID int, FirstName, LastName string, slot int) (int, error) {
|
|
nameCheck := 1 // for now, we approve all names
|
|
QuestFlag := make([]byte, 128)
|
|
SkywayLocationFlag := make([]byte, 16)
|
|
FirstUseFlag := make([]byte, 16)
|
|
|
|
var PlayerID int
|
|
if err := db.Transaction(func(tx *sql.Tx) error {
|
|
// create player
|
|
rows, err := tx.Query(
|
|
"INSERT INTO Players (AccountID, Slot, FirstName, LastName, XCoordinate, YCoordinate, ZCoordinate, Angle, HP, NameCheck, Quests, SkywayLocationFlag, FirstUseFlag) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13) RETURNING PlayerID",
|
|
AccountID, slot, FirstName, LastName, config.SPAWN_X, config.SPAWN_Y, config.SPAWN_Z, 0, config.GetMaxHP(1), nameCheck, QuestFlag, SkywayLocationFlag, FirstUseFlag)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := scan.Row(&PlayerID, rows); err != nil {
|
|
return err
|
|
}
|
|
|
|
// create appearance
|
|
if _, err := tx.Exec("INSERT INTO Appearances (PlayerID) VALUES ($1)", PlayerID); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}); err != nil {
|
|
return -1, nil
|
|
}
|
|
|
|
return PlayerID, nil
|
|
}
|
|
|
|
// TODO: should this operate on the raw packet? should we do validation here or prior?
|
|
func (db *DBHandler) FinishPlayer(character *protocol.SP_CL2LS_REQ_CHAR_CREATE, AccountId int) error {
|
|
return db.Transaction(func(tx *sql.Tx) error {
|
|
// update AppearanceFlag
|
|
_, err := tx.Exec("UPDATE Players SET AppearanceFlag = 1 WHERE PlayerID = $1 AND AccountID = $2 AND AppearanceFlag = 0", character.PCStyle.IPC_UID, AccountId)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// update Appearance
|
|
_, err = tx.Exec("UPDATE Appearances SET Body = $1, EyeColor = $2, FaceStyle = $3, Gender = $4, HairColor = $5, HairStyle = $6, Height = $7, SkinColor = $8 WHERE PlayerID = $9",
|
|
character.PCStyle.IBody,
|
|
character.PCStyle.IEyeColor,
|
|
character.PCStyle.IFaceStyle,
|
|
character.PCStyle.IGender,
|
|
character.PCStyle.IHairColor,
|
|
character.PCStyle.IHairStyle,
|
|
character.PCStyle.IHeight,
|
|
character.PCStyle.ISkinColor,
|
|
character.PCStyle.IPC_UID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// 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, $2, $3, $4, 1)", character.PCStyle.IPC_UID, i+1, items[i], i+1)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
})
|
|
}
|
|
|
|
func (db *DBHandler) FinishTutorial(PlayerID, AccountID int) error {
|
|
_, err := db.Exec("UPDATE Players SET TutorialFlag = 1 WHERE PlayerID = $1 AND AccountID = $2 AND TutorialFlag = 0", PlayerID, AccountID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// TODO: reference openfusion's finishTutorial for their academy specific patches
|
|
return nil
|
|
}
|
|
|
|
// returns the deleted Slot number
|
|
func (db *DBHandler) DeletePlayer(PlayerID, AccountID int) (int, error) {
|
|
row, err := db.Query("DELETE FROM Players WHERE AccountID = $1 AND PlayerID = $2 RETURNING Slot")
|
|
if err != nil {
|
|
return -1, err
|
|
}
|
|
|
|
var slot int
|
|
if err := row.Scan(&slot); err != nil {
|
|
return -1, err
|
|
}
|
|
|
|
return slot, nil
|
|
}
|
|
|
|
const (
|
|
QUERY_PLAYERS = `SELECT
|
|
p.PlayerID, p.AccountID, p.Slot, p.FirstName, p.LastName,
|
|
p.Level, p.Nano1, p.Nano2, p.Nano3,
|
|
p.AppearanceFlag, p.TutorialFlag, p.PayZoneFlag,
|
|
p.XCoordinate, p.YCoordinate, p.ZCoordinate, p.NameCheck,
|
|
p.Angle, p.HP, p.FusionMatter, p.Taros, p.Quests,
|
|
p.BatteryW, p.BatteryN, p.Mentor, p.WarpLocationFlag,
|
|
p.SkywayLocationFlag, p.CurrentMissionID, p.FirstUseFlag,
|
|
a.Body, a.EyeColor, a.FaceStyle, a.Gender, a.HairColor, a.HairStyle,
|
|
a.Height, a.SkinColor, acc.AccountLevel
|
|
FROM Players as p
|
|
INNER JOIN Appearances as a ON p.PlayerID = a.PlayerID
|
|
INNER JOIN Accounts as acc ON p.AccountID = acc.AccountID `
|
|
)
|
|
|
|
func (db *DBHandler) readPlayer(rows *sql.Rows) (*Player, error) {
|
|
plr := Player{ActiveNanoSlotNum: 0}
|
|
|
|
if err := rows.Scan(
|
|
&plr.PlayerID, &plr.AccountID, &plr.Slot, &plr.PCStyle.SzFirstName, &plr.PCStyle.SzLastName,
|
|
&plr.Level, &plr.EquippedNanos[0], &plr.EquippedNanos[1], &plr.EquippedNanos[2],
|
|
&plr.PCStyle2.IAppearanceFlag, &plr.PCStyle2.ITutorialFlag, &plr.PCStyle2.IPayzoneFlag,
|
|
&plr.X, &plr.Y, &plr.Z, &plr.PCStyle.INameCheck,
|
|
&plr.Angle, &plr.HP, &plr.FusionMatter, &plr.Taros, &plr.Quests,
|
|
&plr.BatteryW, &plr.BatteryN, &plr.Mentor, &plr.WarpLocationFlag,
|
|
&plr.SkywayLocationFlag, &plr.CurrentMissionID, &plr.FirstUseFlag,
|
|
&plr.PCStyle.IBody, &plr.PCStyle.IEyeColor, &plr.PCStyle.IFaceStyle, &plr.PCStyle.IGender, &plr.PCStyle.IHairColor, &plr.PCStyle.IHairStyle,
|
|
&plr.PCStyle.IHeight, &plr.PCStyle.ISkinColor, &plr.AccountLevel); err != nil {
|
|
return nil, err
|
|
}
|
|
plr.PCStyle.IPC_UID = int64(plr.PlayerID)
|
|
|
|
inven, err := db.GetPlayerInventorySlots(plr.PlayerID, 0, config.AEQUIP_COUNT+config.AINVEN_COUNT+config.ABANK_COUNT)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// populate player inven
|
|
for i, item := range inven {
|
|
if item.IID == 0 { // skip empty slots
|
|
continue
|
|
}
|
|
|
|
switch {
|
|
case i < config.AEQUIP_COUNT:
|
|
plr.Equip[i] = item
|
|
case i < config.AEQUIP_COUNT+config.AINVEN_COUNT:
|
|
plr.Inven[i-config.AEQUIP_COUNT] = item
|
|
default:
|
|
plr.Bank[i-config.AEQUIP_COUNT-config.AINVEN_COUNT] = item
|
|
}
|
|
}
|
|
|
|
return &plr, nil
|
|
}
|
|
|
|
func (db *DBHandler) GetPlayer(PlayerID int) (*Player, error) {
|
|
rows, err := db.Query(QUERY_PLAYERS+"WHERE p.PlayerID = $1", PlayerID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var plr *Player
|
|
for rows.Next() {
|
|
plr, err = db.readPlayer(rows)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
return plr, nil
|
|
}
|
|
|
|
func (db *DBHandler) GetPlayers(AccountID int) ([]Player, error) {
|
|
rows, err := db.Query(QUERY_PLAYERS+"WHERE p.AccountID = $1", AccountID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var plrs []Player
|
|
for rows.Next() {
|
|
plr, err := db.readPlayer(rows)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
plrs = append(plrs, *plr)
|
|
}
|
|
|
|
return plrs, nil
|
|
}
|