package main import ( "crypto/sha512" "fmt" "time" "zombiezen.com/go/sqlite" ) func authenticateUser(conn *sqlite.Conn, username, password string, allowCreate bool) (bool, uint32, error) { stmt := conn.Prep("SELECT id, password FROM accounts WHERE name = ?") stmt.BindText(1, username) if hasRow, err := stmt.Step(); err != nil { return false, 0, err } else if !hasRow { if allowCreate { return createAccount(conn, username, password) } return false, 0, nil } accountID := stmt.ColumnInt64(0) storedPassword := stmt.ColumnText(1) if verifyPassword(password, storedPassword) { return true, uint32(accountID), nil } return false, 0, nil } func createAccount(conn *sqlite.Conn, username, password string) (bool, uint32, error) { hashedPassword := hashPassword(password) stmt := conn.Prep("INSERT INTO accounts (name, password, created_date) VALUES (?, ?, ?)") stmt.BindText(1, username) stmt.BindText(2, hashedPassword) stmt.BindInt64(3, time.Now().Unix()) if _, err := stmt.Step(); err != nil { return false, 0, err } accountID := conn.LastInsertRowID() return true, uint32(accountID), nil } func hashPassword(password string) string { hash := sha512.Sum512([]byte(password)) return fmt.Sprintf("%x", hash) } func verifyPassword(password, stored string) bool { return hashPassword(password) == stored } func getCharacterList(conn *sqlite.Conn, accountID uint32) ([]*Character, error) { stmt := conn.Prep(` SELECT id, name, race, class, gender, level, current_zone_id, server_id, created_date, last_played FROM login_characters WHERE account_id = ? AND deleted = 0 `) stmt.BindInt64(1, int64(accountID)) var characters []*Character for { hasRow, err := stmt.Step() if err != nil { return nil, err } if !hasRow { break } char := &Character{ ID: uint32(stmt.ColumnInt64(0)), Name: stmt.ColumnText(1), Race: uint8(stmt.ColumnInt64(2)), Class: uint8(stmt.ColumnInt64(3)), Gender: uint8(stmt.ColumnInt64(4)), Level: uint8(stmt.ColumnInt64(5)), Zone: getZoneName(stmt.ColumnInt64(6)), ServerID: uint32(stmt.ColumnInt64(7)), Created: stmt.ColumnInt64(8), LastLogin: stmt.ColumnInt64(9), } characters = append(characters, char) } return characters, nil } func createCharacter(conn *sqlite.Conn, accountID uint32, req *CreateCharacterRequest) (uint32, error) { // Check if name is taken stmt := conn.Prep("SELECT id FROM login_characters WHERE name = ? AND deleted = 0") stmt.BindText(1, req.Name) if hasRow, err := stmt.Step(); err != nil { return 0, err } else if hasRow { return 0, fmt.Errorf("name already taken") } // Create character stmt = conn.Prep(` INSERT INTO login_characters ( account_id, server_id, char_id, name, race, class, gender, deity, body_size, body_age, created_date ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) `) stmt.BindInt64(1, int64(accountID)) stmt.BindInt64(2, int64(req.ServerID)) stmt.BindInt64(3, generateCharacterID()) stmt.BindText(4, req.Name) stmt.BindInt64(5, int64(req.Race)) stmt.BindInt64(6, int64(req.Class)) stmt.BindInt64(7, int64(req.Gender)) stmt.BindInt64(8, int64(req.Deity)) stmt.BindFloat(9, float64(req.BodySize)) stmt.BindFloat(10, float64(req.BodyAge)) stmt.BindInt64(11, time.Now().Unix()) if _, err := stmt.Step(); err != nil { return 0, err } return uint32(conn.LastInsertRowID()), nil } func deleteCharacter(conn *sqlite.Conn, accountID, characterID uint32) error { stmt := conn.Prep(` UPDATE login_characters SET deleted = 1 WHERE id = ? AND account_id = ? `) stmt.BindInt64(1, int64(characterID)) stmt.BindInt64(2, int64(accountID)) _, err := stmt.Step() return err } func generateCharacterID() int64 { return time.Now().UnixNano() / 1000000 // Use timestamp as unique ID } func getZoneName(zoneID int64) string { // Simple zone mapping - in real implementation this would be from database zones := map[int64]string{ 1: "Qeynos", 2: "Freeport", 3: "Kelethin", 4: "Neriak", 5: "Gorowyn", 6: "New Halas", 7: "Queen's Colony", 8: "Outpost of the Overlord", } if name, exists := zones[zoneID]; exists { return name } return "Unknown Zone" } func updateCharacterLastLogin(conn *sqlite.Conn, characterID uint32) error { stmt := conn.Prep("UPDATE login_characters SET last_played = ? WHERE id = ?") stmt.BindInt64(1, time.Now().Unix()) stmt.BindInt64(2, int64(characterID)) _, err := stmt.Step() return err } func getAccountStats(conn *sqlite.Conn) (int, int, error) { // Total accounts stmt := conn.Prep("SELECT COUNT(*) FROM accounts") if hasRow, err := stmt.Step(); err != nil { return 0, 0, err } else if !hasRow { return 0, 0, nil } totalAccounts := int(stmt.ColumnInt64(0)) // Total characters stmt = conn.Prep("SELECT COUNT(*) FROM login_characters WHERE deleted = 0") if hasRow, err := stmt.Step(); err != nil { return 0, 0, err } else if !hasRow { return totalAccounts, 0, nil } totalCharacters := int(stmt.ColumnInt64(0)) return totalAccounts, totalCharacters, nil }