mirror of
https://github.com/CPunch/gopenfusion.git
synced 2025-10-07 18:10:06 +00:00
moved 'core' to 'internal'
This commit is contained in:
70
internal/db/account.go
Normal file
70
internal/db/account.go
Normal file
@@ -0,0 +1,70 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
|
||||
"github.com/CPunch/gopenfusion/internal/protocol"
|
||||
"github.com/georgysavva/scany/v2/sqlscan"
|
||||
)
|
||||
|
||||
type Account struct {
|
||||
AccountID int `db:"accountid"`
|
||||
Login string `db:"login"`
|
||||
Password string `db:"password"`
|
||||
Selected int `db:"selected"`
|
||||
AccountLevel int `db:"accountlevel"`
|
||||
Created int `db:"created"`
|
||||
LastLogin int `db:"lastlogin"`
|
||||
BannedUntil int `db:"banneduntil"`
|
||||
BannedSince int `db:"bannedsince"`
|
||||
BanReason string `db:"banreason"`
|
||||
}
|
||||
|
||||
func (db *DBHandler) NewAccount(Login, Password string) (*Account, error) {
|
||||
hash, err := bcrypt.GenerateFromPassword([]byte(Password), 12)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
row, err := db.Query("INSERT INTO Accounts (Login, Password, AccountLevel) VALUES($1, $2, $3) RETURNING *", Login, hash, protocol.CN_ACCOUNT_LEVEL__USER)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var account Account
|
||||
row.Next()
|
||||
if err := sqlscan.ScanRow(&account, row); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &account, nil
|
||||
}
|
||||
|
||||
var (
|
||||
LoginErrorInvalidID = fmt.Errorf("Invalid Login ID!")
|
||||
LoginErrorInvalidPassword = fmt.Errorf("Invalid ID && Password combo!")
|
||||
)
|
||||
|
||||
func (db *DBHandler) TryLogin(Login, Password string) (*Account, error) {
|
||||
row, err := db.Query("SELECT * FROM Accounts WHERE Login=$1", Login)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var account Account
|
||||
row.Next()
|
||||
if err := sqlscan.ScanRow(&account, row); err != nil {
|
||||
log.Printf("Error scanning row: %v", err)
|
||||
return nil, LoginErrorInvalidID
|
||||
}
|
||||
|
||||
if bcrypt.CompareHashAndPassword([]byte(account.Password), []byte(Password)) != nil {
|
||||
return nil, LoginErrorInvalidPassword
|
||||
}
|
||||
|
||||
// else, login was a success
|
||||
return &account, nil
|
||||
}
|
62
internal/db/inventory.go
Normal file
62
internal/db/inventory.go
Normal file
@@ -0,0 +1,62 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
"github.com/CPunch/gopenfusion/internal/protocol"
|
||||
)
|
||||
|
||||
type Inventory struct {
|
||||
PlayerID int `db:"playerid"`
|
||||
Slot int `db:"slot"`
|
||||
ID int `db:"id"`
|
||||
Type int `db:"type"`
|
||||
Opt int `db:"opt"`
|
||||
TimeLimit int `db:"timelimit"`
|
||||
}
|
||||
|
||||
// start && end are both inclusive
|
||||
func (db *DBHandler) GetPlayerInventorySlots(PlayerID int, start int, end int) ([]protocol.SItemBase, error) {
|
||||
rows, err := db.Query("SELECT Slot, Type, ID, Opt, TimeLimit FROM Inventory WHERE Slot BETWEEN $1 AND $2 AND PlayerID = $3", start, end, PlayerID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
items := make([]protocol.SItemBase, end-start)
|
||||
for rows.Next() {
|
||||
var slot int
|
||||
item := protocol.SItemBase{}
|
||||
|
||||
if err := rows.Scan(
|
||||
&slot, &item.IType, &item.IID, &item.IOpt, &item.ITimeLimit); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
items[slot-start] = item
|
||||
}
|
||||
|
||||
return items, nil
|
||||
}
|
||||
|
||||
// start is inclusive
|
||||
func (db *DBHandler) SetPlayerInventorySlots(PlayerID int, start int, items []protocol.SItemBase) error {
|
||||
return db.Transaction(func(tx *sql.Tx) error {
|
||||
// delete inventory slots
|
||||
_, err := db.Exec("DELETE FROM Inventory WHERE Slot BETWEEN $1 AND $2 AND PlayerID = $3", start, start+len(items)-1, PlayerID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// insert inventory
|
||||
for i, item := range items {
|
||||
if item.IID != 0 {
|
||||
_, err := db.Exec("INSERT INTO Inventory (PlayerID, Slot, ID, Type, Opt, TimeLimit) VALUES ($1, $2, $3, $4, $5, $6)", PlayerID, start+i, item.IID, item.IType, item.IOpt, item.ITimeLimit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
166
internal/db/migrations/new.sql
Normal file
166
internal/db/migrations/new.sql
Normal file
@@ -0,0 +1,166 @@
|
||||
-- this file has been lifted from https://github.com/OpenFusionProject/OpenFusion/bytea/master/sql/tables.sql
|
||||
-- all credit to original contributors!
|
||||
|
||||
CREATE EXTENSION IF NOT EXISTS citext;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS Accounts (
|
||||
AccountID SERIAL NOT NULL,
|
||||
Login CITEXT NOT NULL UNIQUE,
|
||||
Password TEXT NOT NULL,
|
||||
Selected INTEGER DEFAULT 1 NOT NULL,
|
||||
AccountLevel INTEGER NOT NULL,
|
||||
Created INTEGER DEFAULT (extract(EPOCH FROM current_time)) NOT NULL,
|
||||
LastLogin INTEGER DEFAULT (extract(EPOCH FROM current_time)) NOT NULL,
|
||||
BannedUntil INTEGER DEFAULT 0 NOT NULL,
|
||||
BannedSince INTEGER DEFAULT 0 NOT NULL,
|
||||
BanReason TEXT DEFAULT '' NOT NULL,
|
||||
PRIMARY KEY(AccountID)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS Players (
|
||||
PlayerID SERIAL NOT NULL,
|
||||
AccountID INTEGER NOT NULL,
|
||||
FirstName CITEXT NOT NULL,
|
||||
LastName CITEXT NOT NULL,
|
||||
NameCheck INTEGER NOT NULL,
|
||||
Slot INTEGER NOT NULL,
|
||||
Created INTEGER DEFAULT (extract(EPOCH FROM current_time)) NOT NULL,
|
||||
LastLogin INTEGER DEFAULT (extract(EPOCH FROM current_time)) NOT NULL,
|
||||
Level INTEGER DEFAULT 1 NOT NULL,
|
||||
Nano1 INTEGER DEFAULT 0 NOT NULL,
|
||||
Nano2 INTEGER DEFAULT 0 NOT NULL,
|
||||
Nano3 INTEGER DEFAULT 0 NOT NULL,
|
||||
AppearanceFlag INTEGER DEFAULT 0 NOT NULL,
|
||||
TutorialFlag INTEGER DEFAULT 0 NOT NULL,
|
||||
PayZoneFlag INTEGER DEFAULT 0 NOT NULL,
|
||||
XCoordinate INTEGER NOT NULL,
|
||||
YCoordinate INTEGER NOT NULL,
|
||||
ZCoordinate INTEGER NOT NULL,
|
||||
Angle INTEGER NOT NULL,
|
||||
HP INTEGER NOT NULL,
|
||||
FusionMatter INTEGER DEFAULT 0 NOT NULL,
|
||||
Taros INTEGER DEFAULT 0 NOT NULL,
|
||||
BatteryW INTEGER DEFAULT 0 NOT NULL,
|
||||
BatteryN INTEGER DEFAULT 0 NOT NULL,
|
||||
Mentor INTEGER DEFAULT 5 NOT NULL,
|
||||
CurrentMissionID INTEGER DEFAULT 0 NOT NULL,
|
||||
WarpLocationFlag INTEGER DEFAULT 0 NOT NULL,
|
||||
SkywayLocationFlag bytea NOT NULL,
|
||||
FirstUseFlag bytea NOT NULL,
|
||||
Quests bytea NOT NULL,
|
||||
PRIMARY KEY(PlayerID),
|
||||
FOREIGN KEY(AccountID) REFERENCES Accounts(AccountID) ON DELETE CASCADE,
|
||||
UNIQUE (AccountID, Slot),
|
||||
UNIQUE (FirstName, LastName)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS Appearances (
|
||||
PlayerID INTEGER UNIQUE NOT NULL,
|
||||
Body INTEGER DEFAULT 0 NOT NULL,
|
||||
EyeColor INTEGER DEFAULT 1 NOT NULL,
|
||||
FaceStyle INTEGER DEFAULT 1 NOT NULL,
|
||||
Gender INTEGER DEFAULT 1 NOT NULL,
|
||||
HairColor INTEGER DEFAULT 1 NOT NULL,
|
||||
HairStyle INTEGER DEFAULT 1 NOT NULL,
|
||||
Height INTEGER DEFAULT 0 NOT NULL,
|
||||
SkinColor INTEGER DEFAULT 1 NOT NULL,
|
||||
FOREIGN KEY(PlayerID) REFERENCES Players(PlayerID) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS Inventory (
|
||||
PlayerID INTEGER NOT NULL,
|
||||
Slot INTEGER NOT NULL,
|
||||
ID INTEGER NOT NULL,
|
||||
Type INTEGER NOT NULL,
|
||||
Opt INTEGER NOT NULL,
|
||||
TimeLimit INTEGER DEFAULT 0 NOT NULL,
|
||||
FOREIGN KEY(PlayerID) REFERENCES Players(PlayerID) ON DELETE CASCADE,
|
||||
UNIQUE (PlayerID, Slot)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS QuestItems (
|
||||
PlayerID INTEGER NOT NULL,
|
||||
Slot INTEGER NOT NULL,
|
||||
ID INTEGER NOT NULL,
|
||||
Opt INTEGER NOT NULL,
|
||||
FOREIGN KEY(PlayerID) REFERENCES Players(PlayerID) ON DELETE CASCADE,
|
||||
UNIQUE (PlayerID, Slot)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS Nanos (
|
||||
PlayerID INTEGER NOT NULL,
|
||||
ID INTEGER NOT NULL,
|
||||
Skill INTEGER NOT NULL,
|
||||
Stamina INTEGER DEFAULT 150 NOT NULL,
|
||||
FOREIGN KEY(PlayerID) REFERENCES Players(PlayerID) ON DELETE CASCADE,
|
||||
UNIQUE (PlayerID, ID)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS RunningQuests (
|
||||
PlayerID INTEGER NOT NULL,
|
||||
TaskID INTEGER NOT NULL,
|
||||
RemainingNPCCount1 INTEGER NOT NULL,
|
||||
RemainingNPCCount2 INTEGER NOT NULL,
|
||||
RemainingNPCCount3 INTEGER NOT NULL,
|
||||
FOREIGN KEY(PlayerID) REFERENCES Players(PlayerID) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS Buddyships (
|
||||
PlayerAID INTEGER NOT NULL,
|
||||
PlayerBID INTEGER NOT NULL,
|
||||
FOREIGN KEY(PlayerAID) REFERENCES Players(PlayerID) ON DELETE CASCADE,
|
||||
FOREIGN KEY(PlayerBID) REFERENCES Players(PlayerID) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS Blocks (
|
||||
PlayerID INTEGER NOT NULL,
|
||||
BlockedPlayerID INTEGER NOT NULL,
|
||||
FOREIGN KEY(PlayerID) REFERENCES Players(PlayerID) ON DELETE CASCADE,
|
||||
FOREIGN KEY(BlockedPlayerID) REFERENCES Players(PlayerID) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS EmailData (
|
||||
PlayerID INTEGER NOT NULL,
|
||||
MsgIndex INTEGER NOT NULL,
|
||||
ReadFlag INTEGER NOT NULL,
|
||||
ItemFlag INTEGER NOT NULL,
|
||||
SenderID INTEGER NOT NULL,
|
||||
SenderFirstName CITEXT NOT NULL,
|
||||
SenderLastName CITEXT NOT NULL,
|
||||
SubjectLine TEXT NOT NULL,
|
||||
MsgBody TEXT NOT NULL,
|
||||
Taros INTEGER NOT NULL,
|
||||
SendTime INTEGER NOT NULL,
|
||||
DeleteTime INTEGER NOT NULL,
|
||||
FOREIGN KEY(PlayerID) REFERENCES Players(PlayerID) ON DELETE CASCADE,
|
||||
UNIQUE(PlayerID, MsgIndex)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS EmailItems (
|
||||
PlayerID INTEGER NOT NULL,
|
||||
MsgIndex INTEGER NOT NULL,
|
||||
Slot INTEGER NOT NULL,
|
||||
ID INTEGER NOT NULL,
|
||||
Type INTEGER NOT NULL,
|
||||
Opt INTEGER NOT NULL,
|
||||
TimeLimit INTEGER NOT NULL,
|
||||
FOREIGN KEY(PlayerID) REFERENCES Players(PlayerID) ON DELETE CASCADE,
|
||||
UNIQUE (PlayerID, MsgIndex, Slot)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS RaceResults(
|
||||
EPID INTEGER NOT NULL,
|
||||
PlayerID INTEGER NOT NULL,
|
||||
Score INTEGER NOT NULL,
|
||||
RingCount INTEGER NOT NULL,
|
||||
Time INTEGER NOT NULL,
|
||||
Timestamp INTEGER NOT NULL,
|
||||
FOREIGN KEY(PlayerID) REFERENCES Players(PlayerID) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS RedeemedCodes(
|
||||
PlayerID INTEGER NOT NULL,
|
||||
Code TEXT NOT NULL,
|
||||
FOREIGN KEY(PlayerID) REFERENCES Players(PlayerID) ON DELETE CASCADE,
|
||||
UNIQUE (PlayerID, Code)
|
||||
)
|
199
internal/db/players.go
Normal file
199
internal/db/players.go
Normal file
@@ -0,0 +1,199 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
"github.com/CPunch/gopenfusion/config"
|
||||
"github.com/CPunch/gopenfusion/internal/entity"
|
||||
"github.com/CPunch/gopenfusion/internal/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
|
||||
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) (*entity.Player, error) {
|
||||
plr := entity.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) (*entity.Player, error) {
|
||||
rows, err := db.Query(QUERY_PLAYERS+"WHERE p.PlayerID = $1", PlayerID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var plr *entity.Player
|
||||
for rows.Next() {
|
||||
plr, err = db.readPlayer(rows)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return plr, nil
|
||||
}
|
||||
|
||||
func (db *DBHandler) GetPlayers(AccountID int) ([]entity.Player, error) {
|
||||
rows, err := db.Query(QUERY_PLAYERS+"WHERE p.AccountID = $1", AccountID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var plrs []entity.Player
|
||||
for rows.Next() {
|
||||
plr, err := db.readPlayer(rows)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
plrs = append(plrs, *plr)
|
||||
}
|
||||
|
||||
return plrs, nil
|
||||
}
|
73
internal/db/schema.go
Normal file
73
internal/db/schema.go
Normal file
@@ -0,0 +1,73 @@
|
||||
package db
|
||||
|
||||
/*
|
||||
This database has been based off of openfusion's. Databases should be completely interchangable between
|
||||
openfusion and gopenfusion.
|
||||
*/
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
_ "embed"
|
||||
"fmt"
|
||||
|
||||
"github.com/CPunch/gopenfusion/config"
|
||||
_ "github.com/lib/pq"
|
||||
)
|
||||
|
||||
type DBHandler struct {
|
||||
db *sql.DB
|
||||
}
|
||||
|
||||
//go:embed migrations/new.sql
|
||||
var createDBQuery string
|
||||
|
||||
func OpenPostgresDB(dbAddr string) (*DBHandler, error) {
|
||||
fmt := fmt.Sprintf("postgresql://%s:%s@%s/%s?sslmode=disable", config.GetDBUser(), config.GetDBPass(), dbAddr, config.GetDBName())
|
||||
|
||||
db, err := sql.Open("postgres", fmt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &DBHandler{db}, nil
|
||||
}
|
||||
|
||||
func (db *DBHandler) Query(query string, args ...any) (*sql.Rows, error) {
|
||||
return db.db.Query(query, args...)
|
||||
}
|
||||
|
||||
func (db *DBHandler) Exec(query string, args ...any) (sql.Result, error) {
|
||||
return db.db.Exec(query, args...)
|
||||
}
|
||||
|
||||
func (db *DBHandler) Close() error {
|
||||
return db.db.Close()
|
||||
}
|
||||
|
||||
func (db *DBHandler) Setup() error {
|
||||
// create db tables
|
||||
_, err := db.db.Exec(createDBQuery)
|
||||
return err
|
||||
}
|
||||
|
||||
// calls transaction, if transaction returns a non-nil error the transaction is rolled back. otherwise the transaction is committed
|
||||
func (db *DBHandler) Transaction(transaction func(*sql.Tx) error) (err error) {
|
||||
tx, err := db.db.Begin()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
if p := recover(); p != nil {
|
||||
// we panic'd ??? rollback and rethrow
|
||||
tx.Rollback()
|
||||
panic(p)
|
||||
} else if err != nil {
|
||||
tx.Rollback()
|
||||
} else {
|
||||
err = tx.Commit()
|
||||
}
|
||||
}()
|
||||
|
||||
err = transaction(tx)
|
||||
return
|
||||
}
|
Reference in New Issue
Block a user