410 lines
12 KiB
Go
410 lines
12 KiB
Go
package users
|
|
|
|
import (
|
|
"fmt"
|
|
"slices"
|
|
"time"
|
|
|
|
"dk/internal/database"
|
|
"dk/internal/helpers"
|
|
"dk/internal/helpers/scanner"
|
|
|
|
"zombiezen.com/go/sqlite"
|
|
)
|
|
|
|
// User represents a user in the database
|
|
type User struct {
|
|
database.BaseModel
|
|
|
|
ID int `db:"id" json:"id"`
|
|
Username string `db:"username" json:"username"`
|
|
Password string `db:"password" json:"password"`
|
|
Email string `db:"email" json:"email"`
|
|
Verified int `db:"verified" json:"verified"`
|
|
Token string `db:"token" json:"token"`
|
|
Registered int64 `db:"registered" json:"registered"`
|
|
LastOnline int64 `db:"last_online" json:"last_online"`
|
|
Auth int `db:"auth" json:"auth"`
|
|
X int `db:"x" json:"x"`
|
|
Y int `db:"y" json:"y"`
|
|
ClassID int `db:"class_id" json:"class_id"`
|
|
Currently string `db:"currently" json:"currently"`
|
|
Fighting int `db:"fighting" json:"fighting"`
|
|
MonsterID int `db:"monster_id" json:"monster_id"`
|
|
MonsterHP int `db:"monster_hp" json:"monster_hp"`
|
|
MonsterSleep int `db:"monster_sleep" json:"monster_sleep"`
|
|
MonsterImmune int `db:"monster_immune" json:"monster_immune"`
|
|
UberDamage int `db:"uber_damage" json:"uber_damage"`
|
|
UberDefense int `db:"uber_defense" json:"uber_defense"`
|
|
HP int `db:"hp" json:"hp"`
|
|
MP int `db:"mp" json:"mp"`
|
|
TP int `db:"tp" json:"tp"`
|
|
MaxHP int `db:"max_hp" json:"max_hp"`
|
|
MaxMP int `db:"max_mp" json:"max_mp"`
|
|
MaxTP int `db:"max_tp" json:"max_tp"`
|
|
Level int `db:"level" json:"level"`
|
|
Gold int `db:"gold" json:"gold"`
|
|
Exp int `db:"exp" json:"exp"`
|
|
GoldBonus int `db:"gold_bonus" json:"gold_bonus"`
|
|
ExpBonus int `db:"exp_bonus" json:"exp_bonus"`
|
|
Strength int `db:"strength" json:"strength"`
|
|
Dexterity int `db:"dexterity" json:"dexterity"`
|
|
Attack int `db:"attack" json:"attack"`
|
|
Defense int `db:"defense" json:"defense"`
|
|
WeaponID int `db:"weapon_id" json:"weapon_id"`
|
|
ArmorID int `db:"armor_id" json:"armor_id"`
|
|
ShieldID int `db:"shield_id" json:"shield_id"`
|
|
Slot1ID int `db:"slot_1_id" json:"slot_1_id"`
|
|
Slot2ID int `db:"slot_2_id" json:"slot_2_id"`
|
|
Slot3ID int `db:"slot_3_id" json:"slot_3_id"`
|
|
WeaponName string `db:"weapon_name" json:"weapon_name"`
|
|
ArmorName string `db:"armor_name" json:"armor_name"`
|
|
ShieldName string `db:"shield_name" json:"shield_name"`
|
|
Slot1Name string `db:"slot_1_name" json:"slot_1_name"`
|
|
Slot2Name string `db:"slot_2_name" json:"slot_2_name"`
|
|
Slot3Name string `db:"slot_3_name" json:"slot_3_name"`
|
|
DropCode int `db:"drop_code" json:"drop_code"`
|
|
Spells string `db:"spells" json:"spells"`
|
|
Towns string `db:"towns" json:"towns"`
|
|
}
|
|
|
|
func (u *User) GetTableName() string {
|
|
return "users"
|
|
}
|
|
|
|
func (u *User) GetID() int {
|
|
return u.ID
|
|
}
|
|
|
|
func (u *User) SetID(id int) {
|
|
u.ID = id
|
|
}
|
|
|
|
func (u *User) Set(field string, value any) error {
|
|
return database.Set(u, field, value)
|
|
}
|
|
|
|
func (u *User) Save() error {
|
|
return database.Save(u)
|
|
}
|
|
|
|
func (u *User) Delete() error {
|
|
return database.Delete(u)
|
|
}
|
|
|
|
func New() *User {
|
|
now := time.Now().Unix()
|
|
return &User{
|
|
Verified: 0,
|
|
Token: "",
|
|
Registered: now,
|
|
LastOnline: now,
|
|
Auth: 0,
|
|
X: 0,
|
|
Y: 0,
|
|
ClassID: 1,
|
|
Currently: "In Town",
|
|
Fighting: 0,
|
|
HP: 15,
|
|
MP: 0,
|
|
TP: 10,
|
|
MaxHP: 15,
|
|
MaxMP: 0,
|
|
MaxTP: 10,
|
|
Level: 1,
|
|
Gold: 100,
|
|
Exp: 0,
|
|
Strength: 5,
|
|
Dexterity: 5,
|
|
Attack: 5,
|
|
Defense: 5,
|
|
Spells: "",
|
|
Towns: "",
|
|
}
|
|
}
|
|
|
|
var userScanner = scanner.New[User]()
|
|
|
|
func userColumns() string {
|
|
return userScanner.Columns()
|
|
}
|
|
|
|
func scanUser(stmt *sqlite.Stmt) *User {
|
|
user := &User{}
|
|
userScanner.Scan(stmt, user)
|
|
return user
|
|
}
|
|
|
|
func Find(id int) (*User, error) {
|
|
var user *User
|
|
query := `SELECT ` + userColumns() + ` FROM users WHERE id = ?`
|
|
err := database.Query(query, func(stmt *sqlite.Stmt) error {
|
|
user = scanUser(stmt)
|
|
return nil
|
|
}, id)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to find user: %w", err)
|
|
}
|
|
if user == nil {
|
|
return nil, fmt.Errorf("user with ID %d not found", id)
|
|
}
|
|
return user, nil
|
|
}
|
|
|
|
func All() ([]*User, error) {
|
|
var users []*User
|
|
query := `SELECT ` + userColumns() + ` FROM users ORDER BY registered DESC, id DESC`
|
|
err := database.Query(query, func(stmt *sqlite.Stmt) error {
|
|
user := scanUser(stmt)
|
|
users = append(users, user)
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to retrieve all users: %w", err)
|
|
}
|
|
return users, nil
|
|
}
|
|
|
|
func ByUsername(username string) (*User, error) {
|
|
var user *User
|
|
query := `SELECT ` + userColumns() + ` FROM users WHERE LOWER(username) = LOWER(?) LIMIT 1`
|
|
err := database.Query(query, func(stmt *sqlite.Stmt) error {
|
|
user = scanUser(stmt)
|
|
return nil
|
|
}, username)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to find user by username: %w", err)
|
|
}
|
|
if user == nil {
|
|
return nil, fmt.Errorf("user with username '%s' not found", username)
|
|
}
|
|
return user, nil
|
|
}
|
|
|
|
func ByEmail(email string) (*User, error) {
|
|
var user *User
|
|
query := `SELECT ` + userColumns() + ` FROM users WHERE email = ? LIMIT 1`
|
|
err := database.Query(query, func(stmt *sqlite.Stmt) error {
|
|
user = scanUser(stmt)
|
|
return nil
|
|
}, email)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to find user by email: %w", err)
|
|
}
|
|
if user == nil {
|
|
return nil, fmt.Errorf("user with email '%s' not found", email)
|
|
}
|
|
return user, nil
|
|
}
|
|
|
|
func ByLevel(level int) ([]*User, error) {
|
|
var users []*User
|
|
query := `SELECT ` + userColumns() + ` FROM users WHERE level = ? ORDER BY exp DESC, id ASC`
|
|
err := database.Query(query, func(stmt *sqlite.Stmt) error {
|
|
user := scanUser(stmt)
|
|
users = append(users, user)
|
|
return nil
|
|
}, level)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to retrieve users by level: %w", err)
|
|
}
|
|
return users, nil
|
|
}
|
|
|
|
func Online(within time.Duration) ([]*User, error) {
|
|
var users []*User
|
|
cutoff := time.Now().Add(-within).Unix()
|
|
query := `SELECT ` + userColumns() + ` FROM users WHERE last_online >= ? ORDER BY last_online DESC, id ASC`
|
|
err := database.Query(query, func(stmt *sqlite.Stmt) error {
|
|
user := scanUser(stmt)
|
|
users = append(users, user)
|
|
return nil
|
|
}, cutoff)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to retrieve online users: %w", err)
|
|
}
|
|
return users, nil
|
|
}
|
|
|
|
func (u *User) Insert() error {
|
|
columns := `username, password, email, verified, token, registered, last_online, auth,
|
|
x, y, class_id, currently, fighting, monster_id, monster_hp, monster_sleep, monster_immune,
|
|
uber_damage, uber_defense, hp, mp, tp, max_hp, max_mp, max_tp, level, gold, exp,
|
|
gold_bonus, exp_bonus, strength, dexterity, attack, defense, weapon_id, armor_id, shield_id,
|
|
slot_1_id, slot_2_id, slot_3_id, weapon_name, armor_name, shield_name,
|
|
slot_1_name, slot_2_name, slot_3_name, drop_code, spells, towns`
|
|
|
|
values := []any{u.Username, u.Password, u.Email, u.Verified, u.Token,
|
|
u.Registered, u.LastOnline, u.Auth, u.X, u.Y, u.ClassID, u.Currently,
|
|
u.Fighting, u.MonsterID, u.MonsterHP, u.MonsterSleep, u.MonsterImmune,
|
|
u.UberDamage, u.UberDefense, u.HP, u.MP, u.TP, u.MaxHP, u.MaxMP, u.MaxTP,
|
|
u.Level, u.Gold, u.Exp, u.GoldBonus, u.ExpBonus, u.Strength, u.Dexterity,
|
|
u.Attack, u.Defense, u.WeaponID, u.ArmorID, u.ShieldID, u.Slot1ID,
|
|
u.Slot2ID, u.Slot3ID, u.WeaponName, u.ArmorName, u.ShieldName,
|
|
u.Slot1Name, u.Slot2Name, u.Slot3Name, u.DropCode, u.Spells, u.Towns}
|
|
|
|
return database.Insert(u, columns, values...)
|
|
}
|
|
|
|
func (u *User) RegisteredTime() time.Time {
|
|
return time.Unix(u.Registered, 0)
|
|
}
|
|
|
|
func (u *User) LastOnlineTime() time.Time {
|
|
return time.Unix(u.LastOnline, 0)
|
|
}
|
|
|
|
func (u *User) UpdateLastOnline() {
|
|
u.Set("LastOnline", time.Now().Unix())
|
|
}
|
|
|
|
func (u *User) IsVerified() bool {
|
|
return u.Verified == 1
|
|
}
|
|
|
|
func (u *User) IsAdmin() bool {
|
|
return u.Auth >= 4
|
|
}
|
|
|
|
func (u *User) IsModerator() bool {
|
|
return u.Auth >= 3
|
|
}
|
|
|
|
func (u *User) IsFighting() bool {
|
|
return u.Fighting == 1
|
|
}
|
|
|
|
func (u *User) IsAlive() bool {
|
|
return u.HP > 0
|
|
}
|
|
|
|
func (u *User) GetSpellIDs() []int {
|
|
return helpers.StringToInts(u.Spells)
|
|
}
|
|
|
|
func (u *User) SetSpellIDs(spells []int) {
|
|
u.Set("Spells", helpers.IntsToString(spells))
|
|
}
|
|
|
|
func (u *User) HasSpell(spellID int) bool {
|
|
return slices.Contains(u.GetSpellIDs(), spellID)
|
|
}
|
|
|
|
func (u *User) GetTownIDs() []int {
|
|
return helpers.StringToInts(u.Towns)
|
|
}
|
|
|
|
func (u *User) SetTownIDs(towns []int) {
|
|
u.Set("Towns", helpers.IntsToString(towns))
|
|
}
|
|
|
|
func (u *User) HasTownMap(townID int) bool {
|
|
return slices.Contains(u.GetTownIDs(), townID)
|
|
}
|
|
|
|
func (u *User) GetEquipment() map[string]any {
|
|
return map[string]any{
|
|
"weapon": map[string]any{"id": u.WeaponID, "name": u.WeaponName},
|
|
"armor": map[string]any{"id": u.ArmorID, "name": u.ArmorName},
|
|
"shield": map[string]any{"id": u.ShieldID, "name": u.ShieldName},
|
|
"slot1": map[string]any{"id": u.Slot1ID, "name": u.Slot1Name},
|
|
"slot2": map[string]any{"id": u.Slot2ID, "name": u.Slot2Name},
|
|
"slot3": map[string]any{"id": u.Slot3ID, "name": u.Slot3Name},
|
|
}
|
|
}
|
|
|
|
func (u *User) GetStats() map[string]int {
|
|
return map[string]int{
|
|
"level": u.Level,
|
|
"hp": u.HP,
|
|
"mp": u.MP,
|
|
"tp": u.TP,
|
|
"max_hp": u.MaxHP,
|
|
"max_mp": u.MaxMP,
|
|
"max_tp": u.MaxTP,
|
|
"strength": u.Strength,
|
|
"dexterity": u.Dexterity,
|
|
"attack": u.Attack,
|
|
"defense": u.Defense,
|
|
"uber_damage": u.UberDamage,
|
|
"uber_defense": u.UberDefense,
|
|
}
|
|
}
|
|
|
|
func (u *User) GetPosition() (int, int) {
|
|
return u.X, u.Y
|
|
}
|
|
|
|
func (u *User) SetPosition(x, y int) {
|
|
u.Set("X", x)
|
|
u.Set("Y", y)
|
|
}
|
|
|
|
func (u *User) ToMap() map[string]any {
|
|
return map[string]any{
|
|
"ID": u.ID,
|
|
"Username": u.Username,
|
|
"Email": u.Email,
|
|
"Verified": u.Verified,
|
|
"Token": u.Token,
|
|
"Registered": u.Registered,
|
|
"LastOnline": u.LastOnline,
|
|
"Auth": u.Auth,
|
|
"X": u.X,
|
|
"Y": u.Y,
|
|
"ClassID": u.ClassID,
|
|
"Currently": u.Currently,
|
|
"Fighting": u.Fighting,
|
|
"MonsterID": u.MonsterID,
|
|
"MonsterHP": u.MonsterHP,
|
|
"MonsterSleep": u.MonsterSleep,
|
|
"MonsterImmune": u.MonsterImmune,
|
|
"UberDamage": u.UberDamage,
|
|
"UberDefense": u.UberDefense,
|
|
"HP": u.HP,
|
|
"MP": u.MP,
|
|
"TP": u.TP,
|
|
"MaxHP": u.MaxHP,
|
|
"MaxMP": u.MaxMP,
|
|
"MaxTP": u.MaxTP,
|
|
"Level": u.Level,
|
|
"Gold": u.Gold,
|
|
"Exp": u.Exp,
|
|
"GoldBonus": u.GoldBonus,
|
|
"ExpBonus": u.ExpBonus,
|
|
"Strength": u.Strength,
|
|
"Dexterity": u.Dexterity,
|
|
"Attack": u.Attack,
|
|
"Defense": u.Defense,
|
|
"WeaponID": u.WeaponID,
|
|
"ArmorID": u.ArmorID,
|
|
"ShieldID": u.ShieldID,
|
|
"Slot1ID": u.Slot1ID,
|
|
"Slot2ID": u.Slot2ID,
|
|
"Slot3ID": u.Slot3ID,
|
|
"WeaponName": u.WeaponName,
|
|
"ArmorName": u.ArmorName,
|
|
"ShieldName": u.ShieldName,
|
|
"Slot1Name": u.Slot1Name,
|
|
"Slot2Name": u.Slot2Name,
|
|
"Slot3Name": u.Slot3Name,
|
|
"DropCode": u.DropCode,
|
|
"Spells": u.Spells,
|
|
"Towns": u.Towns,
|
|
|
|
// Computed values
|
|
"IsVerified": u.IsVerified(),
|
|
"IsAdmin": u.IsAdmin(),
|
|
"IsModerator": u.IsModerator(),
|
|
"IsFighting": u.IsFighting(),
|
|
"IsAlive": u.IsAlive(),
|
|
"RegisteredTime": u.RegisteredTime(),
|
|
"LastOnlineTime": u.LastOnlineTime(),
|
|
"Equipment": u.GetEquipment(),
|
|
"Stats": u.GetStats(),
|
|
"Position": map[string]int{"X": u.X, "Y": u.Y},
|
|
"SpellIDs": u.GetSpellIDs(),
|
|
"TownIDs": u.GetTownIDs(),
|
|
}
|
|
}
|