mirror of
https://github.com/CPunch/gopenfusion.git
synced 2024-11-25 08:30:37 +00:00
major refactoring: db.Player is now core.Player
- misc. cleanup - core/db/players.go: works with core.Player types, will also grab inventory table
This commit is contained in:
parent
735bdc5b36
commit
5b2a8b838e
@ -1,14 +1,18 @@
|
|||||||
package config
|
package config
|
||||||
|
|
||||||
|
const (
|
||||||
|
AEQUIP_COUNT = 9
|
||||||
|
AINVEN_COUNT = 50
|
||||||
|
ABANK_COUNT = 119
|
||||||
|
|
||||||
|
NANO_COUNT = 37
|
||||||
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
SPAWN_X = 632032
|
SPAWN_X = 632032
|
||||||
SPAWN_Y = 187177
|
SPAWN_Y = 187177
|
||||||
SPAWN_Z = -5500
|
SPAWN_Z = -5500
|
||||||
|
|
||||||
AEQUIP_COUNT = 9
|
|
||||||
AINVEN_COUNT = 50
|
|
||||||
ABANK_COUNT = 119
|
|
||||||
|
|
||||||
LOGIN_PORT = 23000
|
LOGIN_PORT = 23000
|
||||||
SHARD_PORT = 23001
|
SHARD_PORT = 23001
|
||||||
SHARD_IP = "127.0.0.1"
|
SHARD_IP = "127.0.0.1"
|
||||||
|
@ -5,7 +5,7 @@ import (
|
|||||||
|
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
|
|
||||||
"github.com/CPunch/gopenfusion/protocol"
|
"github.com/CPunch/gopenfusion/core/protocol"
|
||||||
"github.com/blockloop/scan"
|
"github.com/blockloop/scan"
|
||||||
)
|
)
|
||||||
|
|
@ -3,7 +3,7 @@ package db
|
|||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
|
||||||
"github.com/CPunch/gopenfusion/protocol"
|
"github.com/CPunch/gopenfusion/core/protocol"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Inventory struct {
|
type Inventory struct {
|
201
core/db/players.go
Normal file
201
core/db/players.go
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
package db
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
|
||||||
|
"github.com/CPunch/gopenfusion/config"
|
||||||
|
"github.com/CPunch/gopenfusion/core"
|
||||||
|
"github.com/CPunch/gopenfusion/core/protocol"
|
||||||
|
"github.com/blockloop/scan"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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
|
||||||
|
row, err := tx.Query(
|
||||||
|
"INSERT INTO Players (AccountID, Slot, FirstName, LastName, XCoordinate, YCoordinate, ZCoordinate, Angle, HP, NameCheck, Quests, SkywayLocationFlag, FirstUseFlag) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) 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, row); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// create appearance
|
||||||
|
if _, err := tx.Exec("INSERT INTO Appearances (PlayerID) VALUES (?)", 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 = ? AND AccountID = ? AND AppearanceFlag = 0", character.PCStyle.IPC_UID, AccountId)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// update Appearance
|
||||||
|
_, err = tx.Exec("UPDATE Appearances SET Body = ?, EyeColor = ?, FaceStyle = ?, Gender = ?, HairColor = ?, HairStyle = ?, Height = ?, SkinColor = ? WHERE PlayerID = ?",
|
||||||
|
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)", character.PCStyle.IPC_UID, i, 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 = ? AND AccountID = ? 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 = ? AND PlayerID = ? RETURNING Slot")
|
||||||
|
if err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var slot int
|
||||||
|
for row.Next() {
|
||||||
|
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) (*core.Player, error) {
|
||||||
|
plr := core.Player{}
|
||||||
|
|
||||||
|
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) (*core.Player, error) {
|
||||||
|
rows, err := db.Query(QUERY_PLAYERS+"WHERE p.PlayerID = ?", PlayerID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var plr *core.Player
|
||||||
|
for rows.Next() {
|
||||||
|
plr, err = db.readPlayer(rows)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return plr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DBHandler) GetPlayers(AccountID int) ([]core.Player, error) {
|
||||||
|
rows, err := db.Query(QUERY_PLAYERS+"WHERE p.AccountID = ?", AccountID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var plrs []core.Player
|
||||||
|
for rows.Next() {
|
||||||
|
plr, err := db.readPlayer(rows)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
plrs = append(plrs, *plr)
|
||||||
|
}
|
||||||
|
|
||||||
|
return plrs, nil
|
||||||
|
}
|
36
core/player.go
Normal file
36
core/player.go
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/CPunch/gopenfusion/config"
|
||||||
|
"github.com/CPunch/gopenfusion/core/protocol"
|
||||||
|
)
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
@ -7,7 +7,7 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
"github.com/CPunch/gopenfusion/protocol/pool"
|
"github.com/CPunch/gopenfusion/core/protocol/pool"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -16,8 +16,8 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type PeerHandler interface {
|
type PeerHandler interface {
|
||||||
HandlePacket(client *CNPeer, typeID uint32, pkt Packet) error
|
HandlePacket(peer *CNPeer, typeID uint32, pkt Packet) error
|
||||||
Disconnect(client *CNPeer)
|
Disconnect(peer *CNPeer)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CNPeer is a simple wrapper for net.Conn connections to send/recv packets over the Fusionfall packet protocol.
|
// CNPeer is a simple wrapper for net.Conn connections to send/recv packets over the Fusionfall packet protocol.
|
226
db/players.go
226
db/players.go
@ -1,226 +0,0 @@
|
|||||||
package db
|
|
||||||
|
|
||||||
import (
|
|
||||||
"database/sql"
|
|
||||||
|
|
||||||
"github.com/CPunch/gopenfusion/config"
|
|
||||||
"github.com/CPunch/gopenfusion/protocol"
|
|
||||||
"github.com/blockloop/scan"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Player struct {
|
|
||||||
PlayerID int
|
|
||||||
AccountID int
|
|
||||||
FirstName string
|
|
||||||
LastName string
|
|
||||||
NameCheck int
|
|
||||||
Slot int
|
|
||||||
Created int
|
|
||||||
LastLogin int
|
|
||||||
Level int
|
|
||||||
Nano1 int
|
|
||||||
Nano2 int
|
|
||||||
Nano3 int
|
|
||||||
AppearanceFlag int
|
|
||||||
TutorialFlag int
|
|
||||||
PayZoneFlag int
|
|
||||||
XCoordinate int
|
|
||||||
YCoordinate int
|
|
||||||
ZCoordinate int
|
|
||||||
Angle int
|
|
||||||
HP int
|
|
||||||
FusionMatter int
|
|
||||||
Taros int
|
|
||||||
BatteryW int
|
|
||||||
BatteryN int
|
|
||||||
Mentor int
|
|
||||||
CurrentMissionID int
|
|
||||||
WarpLocationFlag int
|
|
||||||
SkywayLocationFlag []byte
|
|
||||||
FirstUseFlag []byte
|
|
||||||
Quests []byte
|
|
||||||
Body int /* Appearance tbl start */
|
|
||||||
EyeColor int
|
|
||||||
FaceStyle int
|
|
||||||
Gender int
|
|
||||||
HairColor int
|
|
||||||
HairStyle int
|
|
||||||
Height int
|
|
||||||
SkinColor int /* Appearance tbl end */
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
|
||||||
row, err := tx.Query(
|
|
||||||
"INSERT INTO Players (AccountID, Slot, FirstName, LastName, XCoordinate, YCoordinate, ZCoordinate, Angle, HP, NameCheck, Quests, SkywayLocationFlag, FirstUseFlag) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) 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, row); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// create appearance
|
|
||||||
if _, err := tx.Exec("INSERT INTO Appearances (PlayerID) VALUES (?)", 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 = ? AND AccountID = ? AND AppearanceFlag = 0", character.PCStyle.IPC_UID, AccountId)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// update Appearance
|
|
||||||
_, err = tx.Exec("UPDATE Appearances SET Body = ?, EyeColor = ?, FaceStyle = ?, Gender = ?, HairColor = ?, HairStyle = ?, Height = ?, SkinColor = ? WHERE PlayerID = ?",
|
|
||||||
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)", character.PCStyle.IPC_UID, i, 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 = ? AND AccountID = ? 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 = ? AND PlayerID = ? RETURNING Slot")
|
|
||||||
if err != nil {
|
|
||||||
return -1, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var slot int
|
|
||||||
for row.Next() {
|
|
||||||
if err := row.Scan(&slot); err != nil {
|
|
||||||
return -1, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return slot, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *DBHandler) GetPlayer(PlayerID int) (*Player, error) {
|
|
||||||
rows, err := db.Query(`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
|
|
||||||
FROM Players as p
|
|
||||||
INNER JOIN Appearances as a ON p.PlayerID = a.PlayerID
|
|
||||||
WHERE p.PlayerID = ?`, PlayerID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var plr Player
|
|
||||||
for rows.Next() {
|
|
||||||
if err := rows.Scan(
|
|
||||||
&plr.PlayerID, &plr.AccountID, &plr.Slot, &plr.FirstName, &plr.LastName,
|
|
||||||
&plr.Level, &plr.Nano1, &plr.Nano2, &plr.Nano3,
|
|
||||||
&plr.AppearanceFlag, &plr.TutorialFlag, &plr.PayZoneFlag,
|
|
||||||
&plr.XCoordinate, &plr.YCoordinate, &plr.ZCoordinate, &plr.NameCheck,
|
|
||||||
&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.Body, &plr.EyeColor, &plr.FaceStyle, &plr.Gender, &plr.HairColor, &plr.HairStyle,
|
|
||||||
&plr.Height, &plr.SkinColor); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return &plr, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *DBHandler) GetPlayers(AccountID int) ([]Player, error) {
|
|
||||||
rows, err := db.Query(`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
|
|
||||||
FROM Players as p
|
|
||||||
INNER JOIN Appearances as a ON p.PlayerID = a.PlayerID
|
|
||||||
WHERE p.AccountID = ?`, AccountID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var plrs []Player
|
|
||||||
for rows.Next() {
|
|
||||||
plr := Player{}
|
|
||||||
|
|
||||||
if err := rows.Scan(
|
|
||||||
&plr.PlayerID, &plr.AccountID, &plr.Slot, &plr.FirstName, &plr.LastName,
|
|
||||||
&plr.Level, &plr.Nano1, &plr.Nano2, &plr.Nano3,
|
|
||||||
&plr.AppearanceFlag, &plr.TutorialFlag, &plr.PayZoneFlag,
|
|
||||||
&plr.XCoordinate, &plr.YCoordinate, &plr.ZCoordinate, &plr.NameCheck,
|
|
||||||
&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.Body, &plr.EyeColor, &plr.FaceStyle, &plr.Gender, &plr.HairColor, &plr.HairStyle,
|
|
||||||
&plr.Height, &plr.SkinColor); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
plrs = append(plrs, plr)
|
|
||||||
}
|
|
||||||
|
|
||||||
return plrs, nil
|
|
||||||
}
|
|
2
main.go
2
main.go
@ -4,7 +4,7 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
|
|
||||||
"github.com/CPunch/gopenfusion/config"
|
"github.com/CPunch/gopenfusion/config"
|
||||||
"github.com/CPunch/gopenfusion/db"
|
"github.com/CPunch/gopenfusion/core/db"
|
||||||
"github.com/CPunch/gopenfusion/server"
|
"github.com/CPunch/gopenfusion/server"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1,10 +1,25 @@
|
|||||||
package server
|
package server
|
||||||
|
|
||||||
import "github.com/CPunch/gopenfusion/protocol"
|
import "github.com/CPunch/gopenfusion/core/protocol"
|
||||||
|
|
||||||
func (server *ShardServer) RequestEnter(peer *protocol.CNPeer, pkt protocol.Packet) error {
|
func (server *ShardServer) RequestEnter(peer *protocol.CNPeer, pkt protocol.Packet) (retErr error) {
|
||||||
var enter protocol.SP_CL2FE_REQ_PC_ENTER
|
var enter protocol.SP_CL2FE_REQ_PC_ENTER
|
||||||
pkt.Decode(&enter)
|
pkt.Decode(&enter)
|
||||||
|
|
||||||
|
loginData, err := server.CheckLogin(enter.IEnterSerialKey)
|
||||||
|
if err != nil {
|
||||||
|
// the error codes for P_FE2CL_REP_PC_ENTER_FAIL aren't referenced in the client :(
|
||||||
|
peer.Send(protocol.P_FE2CL_REP_PC_ENTER_FAIL, protocol.SP_FE2CL_REP_PC_ENTER_FAIL{})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
plr, err := server.dbHndlr.GetPlayer(int(loginData.PlayerID))
|
||||||
|
if err != nil {
|
||||||
|
peer.Send(protocol.P_FE2CL_REP_PC_ENTER_FAIL, protocol.SP_FE2CL_REP_PC_ENTER_FAIL{})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
_ = plr
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -6,9 +6,8 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/CPunch/gopenfusion/config"
|
"github.com/CPunch/gopenfusion/config"
|
||||||
"github.com/CPunch/gopenfusion/db"
|
"github.com/CPunch/gopenfusion/core/db"
|
||||||
"github.com/CPunch/gopenfusion/protocol"
|
"github.com/CPunch/gopenfusion/core/protocol"
|
||||||
"github.com/CPunch/gopenfusion/util"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -113,15 +112,14 @@ func (server *LoginServer) Login(peer *protocol.CNPeer, pkt protocol.Packet) err
|
|||||||
// build character list
|
// build character list
|
||||||
charInfo := [4]protocol.SP_LS2CL_REP_CHAR_INFO{}
|
charInfo := [4]protocol.SP_LS2CL_REP_CHAR_INFO{}
|
||||||
for i, plr := range plrs {
|
for i, plr := range plrs {
|
||||||
PCStyle, PCStyle2 := util.Player2PCStyle(&plr)
|
|
||||||
info := protocol.SP_LS2CL_REP_CHAR_INFO{
|
info := protocol.SP_LS2CL_REP_CHAR_INFO{
|
||||||
ISlot: int8(plr.Slot),
|
ISlot: int8(plr.Slot),
|
||||||
ILevel: int16(plr.Level),
|
ILevel: int16(plr.Level),
|
||||||
SPC_Style: PCStyle,
|
SPC_Style: plr.PCStyle,
|
||||||
SPC_Style2: PCStyle2,
|
SPC_Style2: plr.PCStyle2,
|
||||||
IX: int32(plr.XCoordinate),
|
IX: int32(plr.X),
|
||||||
IY: int32(plr.YCoordinate),
|
IY: int32(plr.Y),
|
||||||
IZ: int32(plr.ZCoordinate),
|
IZ: int32(plr.Z),
|
||||||
}
|
}
|
||||||
|
|
||||||
AEquip, err := server.dbHndlr.GetPlayerInventorySlots(plr.PlayerID, 0, config.AEQUIP_COUNT-1)
|
AEquip, err := server.dbHndlr.GetPlayerInventorySlots(plr.PlayerID, 0, config.AEQUIP_COUNT-1)
|
||||||
@ -227,11 +225,10 @@ func (server *LoginServer) CharacterCreate(peer *protocol.CNPeer, pkt protocol.P
|
|||||||
return SendFail(peer)
|
return SendFail(peer)
|
||||||
}
|
}
|
||||||
|
|
||||||
PCStyle, PCStyle2 := util.Player2PCStyle(plr)
|
|
||||||
return peer.Send(protocol.P_LS2CL_REP_CHAR_CREATE_SUCC, protocol.SP_LS2CL_REP_CHAR_CREATE_SUCC{
|
return peer.Send(protocol.P_LS2CL_REP_CHAR_CREATE_SUCC, protocol.SP_LS2CL_REP_CHAR_CREATE_SUCC{
|
||||||
ILevel: int16(plr.Level),
|
ILevel: int16(plr.Level),
|
||||||
SPC_Style: PCStyle,
|
SPC_Style: plr.PCStyle,
|
||||||
SPC_Style2: PCStyle2,
|
SPC_Style2: plr.PCStyle2,
|
||||||
SOn_Item: charPkt.SOn_Item, // if items were faked, we don't really care since the db only stores the sanitized fields
|
SOn_Item: charPkt.SOn_Item, // if items were faked, we don't really care since the db only stores the sanitized fields
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -6,8 +6,8 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/CPunch/gopenfusion/db"
|
"github.com/CPunch/gopenfusion/core/db"
|
||||||
"github.com/CPunch/gopenfusion/protocol"
|
"github.com/CPunch/gopenfusion/core/protocol"
|
||||||
)
|
)
|
||||||
|
|
||||||
type LoginServer struct {
|
type LoginServer struct {
|
||||||
|
@ -7,8 +7,9 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/CPunch/gopenfusion/db"
|
"github.com/CPunch/gopenfusion/core"
|
||||||
"github.com/CPunch/gopenfusion/protocol"
|
"github.com/CPunch/gopenfusion/core/db"
|
||||||
|
"github.com/CPunch/gopenfusion/core/protocol"
|
||||||
)
|
)
|
||||||
|
|
||||||
type LoginMetadata struct {
|
type LoginMetadata struct {
|
||||||
@ -22,7 +23,7 @@ type ShardServer struct {
|
|||||||
port int
|
port int
|
||||||
dbHndlr *db.DBHandler
|
dbHndlr *db.DBHandler
|
||||||
packetHandlers map[uint32]PacketHandler
|
packetHandlers map[uint32]PacketHandler
|
||||||
peers sync.Map // [*protocol.CNPeer]*db.Player
|
peers sync.Map // [*protocol.CNPeer]*core.Player
|
||||||
loginMetadataQueue sync.Map // [int64]*LoginMetadata w/ int64 = serialKey
|
loginMetadataQueue sync.Map // [int64]*LoginMetadata w/ int64 = serialKey
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,19 +89,19 @@ func (server *ShardServer) Connect(peer *protocol.CNPeer) {
|
|||||||
server.peers.Store(peer, nil)
|
server.peers.Store(peer, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (server *ShardServer) JoinPlayer(peer *protocol.CNPeer, player *db.Player) {
|
func (server *ShardServer) JoinPlayer(peer *protocol.CNPeer, player *core.Player) {
|
||||||
server.peers.Store(peer, player)
|
server.peers.Store(peer, player)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Simple wrapper for server.peers.Range, if f returns false the iteration is stopped.
|
// Simple wrapper for server.peers.Range, if f returns false the iteration is stopped.
|
||||||
func (server *ShardServer) RangePeers(f func(peer *protocol.CNPeer, player *db.Player) bool) {
|
func (server *ShardServer) RangePeers(f func(peer *protocol.CNPeer, player *core.Player) bool) {
|
||||||
server.peers.Range(func(key, value any) bool { // simple wrapper to cast the datatypes
|
server.peers.Range(func(key, value any) bool { // simple wrapper to cast the datatypes
|
||||||
peer, ok := key.(*protocol.CNPeer)
|
peer, ok := key.(*protocol.CNPeer)
|
||||||
if !ok { // this should never happen
|
if !ok { // this should never happen
|
||||||
panic(fmt.Errorf("ShardServer.peers has an invalid key: peers[%#v] = %#v", key, value))
|
panic(fmt.Errorf("ShardServer.peers has an invalid key: peers[%#v] = %#v", key, value))
|
||||||
}
|
}
|
||||||
|
|
||||||
player, ok := value.(*db.Player)
|
player, ok := value.(*core.Player)
|
||||||
if !ok { // this should also never happen
|
if !ok { // this should also never happen
|
||||||
panic(fmt.Errorf("ShardServer.peers has an invalid value: peers[%#v] = %#v", key, value))
|
panic(fmt.Errorf("ShardServer.peers has an invalid value: peers[%#v] = %#v", key, value))
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/CPunch/gopenfusion/protocol"
|
"github.com/CPunch/gopenfusion/core/protocol"
|
||||||
)
|
)
|
||||||
|
|
||||||
type PacketHandler func(peer *protocol.CNPeer, pkt protocol.Packet) error
|
type PacketHandler func(peer *protocol.CNPeer, pkt protocol.Packet) error
|
||||||
|
@ -1,28 +0,0 @@
|
|||||||
package util
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/CPunch/gopenfusion/db"
|
|
||||||
"github.com/CPunch/gopenfusion/protocol"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Player2PCStyle(plr *db.Player) (protocol.SPCStyle, protocol.SPCStyle2) {
|
|
||||||
return protocol.SPCStyle{
|
|
||||||
IPC_UID: int64(plr.PlayerID),
|
|
||||||
INameCheck: int8(plr.NameCheck),
|
|
||||||
SzFirstName: plr.FirstName,
|
|
||||||
SzLastName: plr.LastName,
|
|
||||||
IGender: int8(plr.Gender),
|
|
||||||
IFaceStyle: int8(plr.FaceStyle),
|
|
||||||
IHairStyle: int8(plr.HairStyle),
|
|
||||||
IHairColor: int8(plr.HairColor),
|
|
||||||
ISkinColor: int8(plr.SkinColor),
|
|
||||||
IEyeColor: int8(plr.EyeColor),
|
|
||||||
IHeight: int8(plr.Height),
|
|
||||||
IBody: int8(plr.Body),
|
|
||||||
},
|
|
||||||
protocol.SPCStyle2{
|
|
||||||
IAppearanceFlag: int8(plr.AppearanceFlag),
|
|
||||||
ITutorialFlag: int8(plr.TutorialFlag),
|
|
||||||
IPayzoneFlag: int8(plr.PayZoneFlag),
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user