mirror of
				https://github.com/CPunch/gopenfusion.git
				synced 2025-11-03 22:50:20 +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:
		@@ -1,14 +1,18 @@
 | 
			
		||||
package config
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	AEQUIP_COUNT = 9
 | 
			
		||||
	AINVEN_COUNT = 50
 | 
			
		||||
	ABANK_COUNT  = 119
 | 
			
		||||
 | 
			
		||||
	NANO_COUNT = 37
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	SPAWN_X = 632032
 | 
			
		||||
	SPAWN_Y = 187177
 | 
			
		||||
	SPAWN_Z = -5500
 | 
			
		||||
 | 
			
		||||
	AEQUIP_COUNT = 9
 | 
			
		||||
	AINVEN_COUNT = 50
 | 
			
		||||
	ABANK_COUNT  = 119
 | 
			
		||||
 | 
			
		||||
	LOGIN_PORT = 23000
 | 
			
		||||
	SHARD_PORT = 23001
 | 
			
		||||
	SHARD_IP   = "127.0.0.1"
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,7 @@ import (
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/crypto/bcrypt"
 | 
			
		||||
 | 
			
		||||
	"github.com/CPunch/gopenfusion/protocol"
 | 
			
		||||
	"github.com/CPunch/gopenfusion/core/protocol"
 | 
			
		||||
	"github.com/blockloop/scan"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -3,7 +3,7 @@ package db
 | 
			
		||||
import (
 | 
			
		||||
	"database/sql"
 | 
			
		||||
 | 
			
		||||
	"github.com/CPunch/gopenfusion/protocol"
 | 
			
		||||
	"github.com/CPunch/gopenfusion/core/protocol"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
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"
 | 
			
		||||
	"net"
 | 
			
		||||
 | 
			
		||||
	"github.com/CPunch/gopenfusion/protocol/pool"
 | 
			
		||||
	"github.com/CPunch/gopenfusion/core/protocol/pool"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
@@ -16,8 +16,8 @@ const (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type PeerHandler interface {
 | 
			
		||||
	HandlePacket(client *CNPeer, typeID uint32, pkt Packet) error
 | 
			
		||||
	Disconnect(client *CNPeer)
 | 
			
		||||
	HandlePacket(peer *CNPeer, typeID uint32, pkt Packet) error
 | 
			
		||||
	Disconnect(peer *CNPeer)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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"
 | 
			
		||||
 | 
			
		||||
	"github.com/CPunch/gopenfusion/config"
 | 
			
		||||
	"github.com/CPunch/gopenfusion/db"
 | 
			
		||||
	"github.com/CPunch/gopenfusion/core/db"
 | 
			
		||||
	"github.com/CPunch/gopenfusion/server"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +1,25 @@
 | 
			
		||||
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
 | 
			
		||||
	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
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -6,9 +6,8 @@ import (
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/CPunch/gopenfusion/config"
 | 
			
		||||
	"github.com/CPunch/gopenfusion/db"
 | 
			
		||||
	"github.com/CPunch/gopenfusion/protocol"
 | 
			
		||||
	"github.com/CPunch/gopenfusion/util"
 | 
			
		||||
	"github.com/CPunch/gopenfusion/core/db"
 | 
			
		||||
	"github.com/CPunch/gopenfusion/core/protocol"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
@@ -113,15 +112,14 @@ func (server *LoginServer) Login(peer *protocol.CNPeer, pkt protocol.Packet) err
 | 
			
		||||
	// build character list
 | 
			
		||||
	charInfo := [4]protocol.SP_LS2CL_REP_CHAR_INFO{}
 | 
			
		||||
	for i, plr := range plrs {
 | 
			
		||||
		PCStyle, PCStyle2 := util.Player2PCStyle(&plr)
 | 
			
		||||
		info := protocol.SP_LS2CL_REP_CHAR_INFO{
 | 
			
		||||
			ISlot:      int8(plr.Slot),
 | 
			
		||||
			ILevel:     int16(plr.Level),
 | 
			
		||||
			SPC_Style:  PCStyle,
 | 
			
		||||
			SPC_Style2: PCStyle2,
 | 
			
		||||
			IX:         int32(plr.XCoordinate),
 | 
			
		||||
			IY:         int32(plr.YCoordinate),
 | 
			
		||||
			IZ:         int32(plr.ZCoordinate),
 | 
			
		||||
			SPC_Style:  plr.PCStyle,
 | 
			
		||||
			SPC_Style2: plr.PCStyle2,
 | 
			
		||||
			IX:         int32(plr.X),
 | 
			
		||||
			IY:         int32(plr.Y),
 | 
			
		||||
			IZ:         int32(plr.Z),
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		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)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	PCStyle, PCStyle2 := util.Player2PCStyle(plr)
 | 
			
		||||
	return peer.Send(protocol.P_LS2CL_REP_CHAR_CREATE_SUCC, protocol.SP_LS2CL_REP_CHAR_CREATE_SUCC{
 | 
			
		||||
		ILevel:     int16(plr.Level),
 | 
			
		||||
		SPC_Style:  PCStyle,
 | 
			
		||||
		SPC_Style2: PCStyle2,
 | 
			
		||||
		SPC_Style:  plr.PCStyle,
 | 
			
		||||
		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
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -6,8 +6,8 @@ import (
 | 
			
		||||
	"net"
 | 
			
		||||
	"sync"
 | 
			
		||||
 | 
			
		||||
	"github.com/CPunch/gopenfusion/db"
 | 
			
		||||
	"github.com/CPunch/gopenfusion/protocol"
 | 
			
		||||
	"github.com/CPunch/gopenfusion/core/db"
 | 
			
		||||
	"github.com/CPunch/gopenfusion/core/protocol"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type LoginServer struct {
 | 
			
		||||
 
 | 
			
		||||
@@ -7,8 +7,9 @@ import (
 | 
			
		||||
	"sync"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/CPunch/gopenfusion/db"
 | 
			
		||||
	"github.com/CPunch/gopenfusion/protocol"
 | 
			
		||||
	"github.com/CPunch/gopenfusion/core"
 | 
			
		||||
	"github.com/CPunch/gopenfusion/core/db"
 | 
			
		||||
	"github.com/CPunch/gopenfusion/core/protocol"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type LoginMetadata struct {
 | 
			
		||||
@@ -22,7 +23,7 @@ type ShardServer struct {
 | 
			
		||||
	port               int
 | 
			
		||||
	dbHndlr            *db.DBHandler
 | 
			
		||||
	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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -88,19 +89,19 @@ func (server *ShardServer) Connect(peer *protocol.CNPeer) {
 | 
			
		||||
	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)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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
 | 
			
		||||
		peer, ok := key.(*protocol.CNPeer)
 | 
			
		||||
		if !ok { // this should never happen
 | 
			
		||||
			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
 | 
			
		||||
			panic(fmt.Errorf("ShardServer.peers has an invalid value: peers[%#v] = %#v", key, value))
 | 
			
		||||
		}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
package server
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/CPunch/gopenfusion/protocol"
 | 
			
		||||
	"github.com/CPunch/gopenfusion/core/protocol"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
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),
 | 
			
		||||
		}
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user