501 lines
12 KiB
Go
501 lines
12 KiB
Go
/*
|
|
Package users is the active record implementation for user accounts in the game.
|
|
|
|
The users package provides comprehensive user management for the game, including authentication, character progression, inventory, equipment, and game state management. It handles all aspects of player accounts from registration to advanced gameplay features.
|
|
|
|
# Basic Usage
|
|
|
|
To retrieve a user by ID:
|
|
|
|
user, err := users.Find(db, 1)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
fmt.Printf("User: %s (Level %d)\n", user.Username, user.Level)
|
|
|
|
To find a user by username:
|
|
|
|
user, err := users.ByUsername(db, "playerName")
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
To find a user by email:
|
|
|
|
user, err := users.ByEmail(db, "player@example.com")
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
# Creating Users with Builder Pattern
|
|
|
|
The package provides a comprehensive builder for creating new user accounts:
|
|
|
|
## Basic User Creation
|
|
|
|
user, err := users.NewBuilder(db).
|
|
WithUsername("newplayer").
|
|
WithPassword("hashedPassword").
|
|
WithEmail("newplayer@example.com").
|
|
WithClassID(1).
|
|
Create()
|
|
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
fmt.Printf("Created user with ID: %d\n", user.ID)
|
|
|
|
## Advanced User Creation
|
|
|
|
user, err := users.NewBuilder(db).
|
|
WithUsername("hero").
|
|
WithPassword("secureHash").
|
|
WithEmail("hero@example.com").
|
|
WithVerified(true).
|
|
WithClassID(2).
|
|
WithPosition(100, -50).
|
|
WithLevel(5).
|
|
WithGold(500).
|
|
WithStats(8, 7, 10, 9). // strength, dex, attack, defense
|
|
WithHP(30, 30).
|
|
WithMP(20, 20).
|
|
WithSpells([]string{"1", "3", "5"}).
|
|
WithTowns([]string{"1", "2"}).
|
|
AsAdmin().
|
|
Create()
|
|
|
|
The builder automatically sets sensible defaults for all fields if not specified.
|
|
|
|
# User Management
|
|
|
|
## Authentication and Verification
|
|
|
|
user, _ := users.Find(db, userID)
|
|
|
|
// Check verification status
|
|
if user.IsVerified() {
|
|
fmt.Println("User email is verified")
|
|
}
|
|
|
|
// Check authorization levels
|
|
if user.IsAdmin() {
|
|
fmt.Println("User has admin privileges")
|
|
}
|
|
if user.IsModerator() {
|
|
fmt.Println("User has moderator privileges")
|
|
}
|
|
|
|
## Activity Tracking
|
|
|
|
// Update last online time
|
|
user.UpdateLastOnline()
|
|
user.Save()
|
|
|
|
// Get activity information
|
|
registered := user.RegisteredTime()
|
|
lastOnline := user.LastOnlineTime()
|
|
|
|
fmt.Printf("Registered: %s\n", registered.Format("Jan 2, 2006"))
|
|
fmt.Printf("Last online: %s\n", lastOnline.Format("Jan 2 15:04"))
|
|
|
|
# Character Management
|
|
|
|
## Stats and Progression
|
|
|
|
user, _ := users.Find(db, userID)
|
|
|
|
// Get character stats
|
|
stats := user.GetStats()
|
|
fmt.Printf("Level %d: HP %d/%d, MP %d/%d\n",
|
|
stats["level"], stats["hp"], stats["max_hp"],
|
|
stats["mp"], stats["max_mp"])
|
|
|
|
// Update character progression
|
|
user.Level = 10
|
|
user.Exp = 5000
|
|
user.MaxHP = 50
|
|
user.HP = 50
|
|
user.Save()
|
|
|
|
## Position and Movement
|
|
|
|
// Get current position
|
|
x, y := user.GetPosition()
|
|
fmt.Printf("Player at (%d, %d)\n", x, y)
|
|
|
|
// Move player
|
|
user.SetPosition(newX, newY)
|
|
user.Currently = "Exploring the forest"
|
|
user.Save()
|
|
|
|
## Combat Status
|
|
|
|
if user.IsFighting() {
|
|
fmt.Printf("Fighting monster ID %d (HP: %d)\n",
|
|
user.MonsterID, user.MonsterHP)
|
|
}
|
|
|
|
if user.IsAlive() {
|
|
fmt.Printf("Player has %d HP remaining\n", user.HP)
|
|
}
|
|
|
|
# Spell System
|
|
|
|
## Spell Management
|
|
|
|
user, _ := users.Find(db, userID)
|
|
|
|
// Get known spells
|
|
spells := user.GetSpellIDs()
|
|
fmt.Printf("Player knows %d spells: %v\n", len(spells), spells)
|
|
|
|
// Check if player knows a specific spell
|
|
if user.HasSpell("5") {
|
|
fmt.Println("Player knows spell 5")
|
|
}
|
|
|
|
// Learn new spells
|
|
newSpells := append(spells, "7", "8")
|
|
user.SetSpellIDs(newSpells)
|
|
user.Save()
|
|
|
|
## Spell Integration
|
|
|
|
func castSpell(db *database.DB, userID int, spellID string) error {
|
|
user, err := users.Find(db, userID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if !user.HasSpell(spellID) {
|
|
return fmt.Errorf("user doesn't know spell %s", spellID)
|
|
}
|
|
|
|
// Spell casting logic here...
|
|
return nil
|
|
}
|
|
|
|
# Town and Travel System
|
|
|
|
## Town Visits
|
|
|
|
user, _ := users.Find(db, userID)
|
|
|
|
// Get visited towns
|
|
towns := user.GetTownIDs()
|
|
fmt.Printf("Visited %d towns: %v\n", len(towns), towns)
|
|
|
|
// Check if player has visited a town
|
|
if user.HasVisitedTown("3") {
|
|
fmt.Println("Player has been to town 3")
|
|
}
|
|
|
|
// Visit new town
|
|
visitedTowns := append(towns, "4")
|
|
user.SetTownIDs(visitedTowns)
|
|
user.Save()
|
|
|
|
## Travel Integration
|
|
|
|
func visitTown(db *database.DB, userID int, townID string) error {
|
|
user, err := users.Find(db, userID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Add town to visited list if not already there
|
|
if !user.HasVisitedTown(townID) {
|
|
towns := user.GetTownIDs()
|
|
user.SetTownIDs(append(towns, townID))
|
|
}
|
|
|
|
// Update position and status
|
|
// town coordinates would be looked up here
|
|
user.Currently = fmt.Sprintf("In town %s", townID)
|
|
return user.Save()
|
|
}
|
|
|
|
# Equipment System
|
|
|
|
## Equipment Management
|
|
|
|
user, _ := users.Find(db, userID)
|
|
|
|
// Get all equipment
|
|
equipment := user.GetEquipment()
|
|
weapon := equipment["weapon"].(map[string]any)
|
|
armor := equipment["armor"].(map[string]any)
|
|
|
|
fmt.Printf("Weapon: %s (ID: %d)\n", weapon["name"], weapon["id"])
|
|
fmt.Printf("Armor: %s (ID: %d)\n", armor["name"], armor["id"])
|
|
|
|
// Equip new items
|
|
user.WeaponID = 15
|
|
user.WeaponName = "Dragon Sword"
|
|
user.ArmorID = 8
|
|
user.ArmorName = "Steel Plate"
|
|
user.Save()
|
|
|
|
# Query Operations
|
|
|
|
## Level-Based Queries
|
|
|
|
// Get all players at a specific level
|
|
level5Players, err := users.ByLevel(db, 5)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
fmt.Printf("Level 5 players (%d):\n", len(level5Players))
|
|
for _, player := range level5Players {
|
|
fmt.Printf("- %s (EXP: %d)\n", player.Username, player.Exp)
|
|
}
|
|
|
|
## Online Status Queries
|
|
|
|
// Get players online in the last hour
|
|
onlinePlayers, err := users.Online(db, time.Hour)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
fmt.Printf("Players online in last hour (%d):\n", len(onlinePlayers))
|
|
for _, player := range onlinePlayers {
|
|
lastSeen := time.Since(player.LastOnlineTime())
|
|
fmt.Printf("- %s (last seen %v ago)\n", player.Username, lastSeen)
|
|
}
|
|
|
|
# Database Schema
|
|
|
|
The users table contains extensive character and game state information:
|
|
|
|
CREATE TABLE users (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
username TEXT NOT NULL,
|
|
password TEXT NOT NULL,
|
|
email TEXT NOT NULL,
|
|
verified INTEGER NOT NULL DEFAULT 0,
|
|
token TEXT NOT NULL DEFAULT '',
|
|
registered INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
last_online INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
auth INTEGER NOT NULL DEFAULT 0,
|
|
x INTEGER NOT NULL DEFAULT 0,
|
|
y INTEGER NOT NULL DEFAULT 0,
|
|
class_id INTEGER NOT NULL DEFAULT 0,
|
|
currently TEXT NOT NULL DEFAULT 'In Town',
|
|
fighting INTEGER NOT NULL DEFAULT 0,
|
|
-- Combat state fields
|
|
monster_id INTEGER NOT NULL DEFAULT 0,
|
|
monster_hp INTEGER NOT NULL DEFAULT 0,
|
|
monster_sleep INTEGER NOT NULL DEFAULT 0,
|
|
monster_immune INTEGER NOT NULL DEFAULT 0,
|
|
uber_damage INTEGER NOT NULL DEFAULT 0,
|
|
uber_defense INTEGER NOT NULL DEFAULT 0,
|
|
-- Character stats
|
|
hp INTEGER NOT NULL DEFAULT 15,
|
|
mp INTEGER NOT NULL DEFAULT 0,
|
|
tp INTEGER NOT NULL DEFAULT 10,
|
|
max_hp INTEGER NOT NULL DEFAULT 15,
|
|
max_mp INTEGER NOT NULL DEFAULT 0,
|
|
max_tp INTEGER NOT NULL DEFAULT 10,
|
|
level INTEGER NOT NULL DEFAULT 1,
|
|
gold INTEGER NOT NULL DEFAULT 100,
|
|
exp INTEGER NOT NULL DEFAULT 0,
|
|
gold_bonus INTEGER NOT NULL DEFAULT 0,
|
|
exp_bonus INTEGER NOT NULL DEFAULT 0,
|
|
strength INTEGER NOT NULL DEFAULT 5,
|
|
dexterity INTEGER NOT NULL DEFAULT 5,
|
|
attack INTEGER NOT NULL DEFAULT 5,
|
|
defense INTEGER NOT NULL DEFAULT 5,
|
|
-- Equipment
|
|
weapon_id INTEGER NOT NULL DEFAULT 0,
|
|
armor_id INTEGER NOT NULL DEFAULT 0,
|
|
shield_id INTEGER NOT NULL DEFAULT 0,
|
|
slot_1_id INTEGER NOT NULL DEFAULT 0,
|
|
slot_2_id INTEGER NOT NULL DEFAULT 0,
|
|
slot_3_id INTEGER NOT NULL DEFAULT 0,
|
|
weapon_name TEXT NOT NULL DEFAULT '',
|
|
armor_name TEXT NOT NULL DEFAULT '',
|
|
shield_name TEXT NOT NULL DEFAULT '',
|
|
slot_1_name TEXT NOT NULL DEFAULT '',
|
|
slot_2_name TEXT NOT NULL DEFAULT '',
|
|
slot_3_name TEXT NOT NULL DEFAULT '',
|
|
-- Game state
|
|
drop_code INTEGER NOT NULL DEFAULT 0,
|
|
spells TEXT NOT NULL DEFAULT '',
|
|
towns TEXT NOT NULL DEFAULT ''
|
|
)
|
|
|
|
# Advanced Features
|
|
|
|
## Character Progression
|
|
|
|
func levelUpCharacter(user *users.User, newLevel int) {
|
|
user.Level = newLevel
|
|
|
|
// Increase base stats
|
|
user.MaxHP += 5
|
|
user.HP = user.MaxHP // Full heal on level up
|
|
user.MaxMP += 2
|
|
user.MP = user.MaxMP
|
|
|
|
// Stat bonuses
|
|
user.Strength++
|
|
user.Attack++
|
|
user.Defense++
|
|
|
|
user.Save()
|
|
}
|
|
|
|
## Combat Integration
|
|
|
|
func startCombat(user *users.User, monsterID int) error {
|
|
if user.IsFighting() {
|
|
return fmt.Errorf("already in combat")
|
|
}
|
|
|
|
user.Fighting = 1
|
|
user.MonsterID = monsterID
|
|
// monster HP would be looked up from monsters table
|
|
user.MonsterHP = 50
|
|
user.Currently = "Fighting"
|
|
|
|
return user.Save()
|
|
}
|
|
|
|
func endCombat(user *users.User, won bool) error {
|
|
user.Fighting = 0
|
|
user.MonsterID = 0
|
|
user.MonsterHP = 0
|
|
user.MonsterSleep = 0
|
|
user.MonsterImmune = 0
|
|
|
|
if won {
|
|
user.Currently = "Victorious"
|
|
// Award experience and gold
|
|
} else {
|
|
user.Currently = "Defeated"
|
|
user.HP = 0 // Player defeated
|
|
}
|
|
|
|
return user.Save()
|
|
}
|
|
|
|
## Administrative Functions
|
|
|
|
func promoteUser(db *database.DB, username string, authLevel int) error {
|
|
user, err := users.ByUsername(db, username)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
user.Auth = authLevel
|
|
return user.Save()
|
|
}
|
|
|
|
func getUsersByAuthLevel(db *database.DB, minAuth int) ([]*users.User, error) {
|
|
allUsers, err := users.All(db)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var authorizedUsers []*users.User
|
|
for _, user := range allUsers {
|
|
if user.Auth >= minAuth {
|
|
authorizedUsers = append(authorizedUsers, user)
|
|
}
|
|
}
|
|
|
|
return authorizedUsers, nil
|
|
}
|
|
|
|
# Performance Considerations
|
|
|
|
The users table is large and frequently accessed. Consider:
|
|
|
|
## Efficient Queries
|
|
|
|
// Use specific lookups when possible
|
|
user, _ := users.ByUsername(db, username) // Uses index
|
|
user, _ := users.ByEmail(db, email) // Uses index
|
|
|
|
// Limit results for admin interfaces
|
|
onlineUsers, _ := users.Online(db, time.Hour) // Bounded by time
|
|
levelUsers, _ := users.ByLevel(db, targetLevel) // Bounded by level
|
|
|
|
## Caching Strategies
|
|
|
|
// Cache frequently accessed user data
|
|
type UserCache struct {
|
|
users map[int]*users.User
|
|
mutex sync.RWMutex
|
|
}
|
|
|
|
func (c *UserCache) GetUser(db *database.DB, id int) (*users.User, error) {
|
|
c.mutex.RLock()
|
|
if user, ok := c.users[id]; ok {
|
|
c.mutex.RUnlock()
|
|
return user, nil
|
|
}
|
|
c.mutex.RUnlock()
|
|
|
|
user, err := users.Find(db, id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
c.mutex.Lock()
|
|
c.users[id] = user
|
|
c.mutex.Unlock()
|
|
|
|
return user, nil
|
|
}
|
|
|
|
# Integration Examples
|
|
|
|
## Session Management
|
|
|
|
func authenticateUser(db *database.DB, username, password string) (*users.User, error) {
|
|
user, err := users.ByUsername(db, username)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("user not found")
|
|
}
|
|
|
|
if !user.IsVerified() {
|
|
return nil, fmt.Errorf("email not verified")
|
|
}
|
|
|
|
// Verify password (implement password checking)
|
|
if !verifyPassword(user.Password, password) {
|
|
return nil, fmt.Errorf("invalid password")
|
|
}
|
|
|
|
// Update last online
|
|
user.UpdateLastOnline()
|
|
user.Save()
|
|
|
|
return user, nil
|
|
}
|
|
|
|
## Game State Management
|
|
|
|
func saveGameState(user *users.User, gameData GameState) error {
|
|
user.X = gameData.X
|
|
user.Y = gameData.Y
|
|
user.HP = gameData.HP
|
|
user.MP = gameData.MP
|
|
user.Currently = gameData.Status
|
|
|
|
if gameData.InCombat {
|
|
user.Fighting = 1
|
|
user.MonsterID = gameData.MonsterID
|
|
user.MonsterHP = gameData.MonsterHP
|
|
}
|
|
|
|
return user.Save()
|
|
}
|
|
|
|
The users package provides comprehensive player account management with support for all game mechanics including character progression, combat, equipment, spells, and world exploration.
|
|
*/
|
|
package users
|