users

package
v0.9.8 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 4, 2026 License: MIT Imports: 34 Imported by: 0

Documentation

Index

Constants

View Source
const (
	IndexVersion           = 2
	IndexLineTerminatorV1  = byte(10) // "\n"
	IndexRecordSizeV1      = 89
	FixedHeaderTotalLength = 100 // 99 bytes header content + 1 byte newline
)
View Source
const (
	LogMinAllocation = 50
	LogMaxAllocation = 1000
)

Variables

View Source
var (
	ErrIndexMissing        = errors.New(`index does not exist`)
	ErrUserFilesOldFormat  = errors.New(`user files are in old format of username.yaml`)
	ErrIndexVersionInvalid = errors.New(`version out of date.`)
	ErrSearchNameTooLong   = errors.New(`search name provided is too long`)
	ErrNotFound            = errors.New("user not found")
)
View Source
var (
	// immutable roles
	RoleGuest string = "guest"
	RoleUser  string = "user"
	RoleMod   string = "mod"
	RoleAdmin string = "admin"
)
View Source
var (
	ErrBothFilesExist = errors.New("could not migrate due to both file formats existing")
)

Functions

func AddFunctionExporter added in v0.9.8

func AddFunctionExporter(f FunctionExporter)

AddFunctionExporter registers a function exporter (called from main.go with plugins.GetPluginRegistry()).

func CharacterNameSearch

func CharacterNameSearch(nameToFind string) (foundUserId int, foundUserName string)

CharacterNameSearch returns the userId and username for the given character name. It consults the in-memory CharacterIndex, which is populated at startup with every active character name and, when the alt-characters module is loaded, with all stored alt names as well.

func CopyoverContributor added in v0.9.8

func CopyoverContributor() copyover.Contributor

CopyoverContributor returns the users contributor for registration.

func CreateUser

func CreateUser(u *UserRecord) error

First time creating a user.

func DeleteUser added in v0.9.8

func DeleteUser(userId int) error

DeleteUser permanently removes a user record, their YAML file, and all index entries. It refuses to delete a user that is currently online.

func DoFilenameMigrationV1

func DoFilenameMigrationV1() error

func DoUserMigrations

func DoUserMigrations()

func Exists

func Exists(name string) bool

func FindUserId

func FindUserId(username string) int

func GetConnectionId

func GetConnectionId(userId int) connections.ConnectionId

func GetConnectionIds

func GetConnectionIds(userIds []int) []connections.ConnectionId

func GetExpiredLinkDeadUsers added in v0.9.8

func GetExpiredLinkDeadUsers(expirationTurn uint64) []int

Returns a slice of userId's These userId's are link-dead players that have reached expiration

func GetExportedFunction added in v0.9.8

func GetExportedFunction(fName string) (any, bool)

GetExportedFunction looks up a named function across all registered exporters.

func GetMemoryUsage

func GetMemoryUsage() map[string]util.MemoryResult

func GetOnlineUserIds

func GetOnlineUserIds() []int

func GetUniqueUserId

func GetUniqueUserId() int

func IsLinkDeadConnection added in v0.9.8

func IsLinkDeadConnection(connectionId connections.ConnectionId) bool

func LogOutUserByConnectionId

func LogOutUserByConnectionId(connectionId connections.ConnectionId) error

func RemoveLinkDeadConnection added in v0.9.8

func RemoveLinkDeadConnection(connectionId connections.ConnectionId)

func RemoveLinkDeadUser added in v0.9.8

func RemoveLinkDeadUser(userId int)

func RemoveTestUser added in v0.9.8

func RemoveTestUser(userId int)

RemoveTestUser removes a user from the active users map. For testing only.

func ResetActiveUsers added in v0.9.8

func ResetActiveUsers()

ResetActiveUsers resets the user manager to a clean state. For testing only.

func SaveAllUsers

func SaveAllUsers(isAutoSave ...bool)

func SaveUser

func SaveUser(u UserRecord, isAutoSave ...bool) error

func SearchOfflineUsers

func SearchOfflineUsers(searchFunc func(u *UserRecord) bool)

Loads all user recvords and runs against a function. Stops searching if false is returned.

func SetLinkDeadUser added in v0.9.8

func SetLinkDeadUser(userId int)

func SetTestConnection added in v0.9.8

func SetTestConnection(connectionId connections.ConnectionId, userId int)

SetTestConnection registers a connection-to-user mapping. Must be called alongside SetTestUser so that GetByConnectionId resolves correctly. For testing only.

func SetTestUser added in v0.9.8

func SetTestUser(u *UserRecord)

SetTestUser adds a user directly to the active users map. For testing only.

func UpdateOnlineUser added in v0.9.8

func UpdateOnlineUser(updated UserRecord)

UpdateOnlineUser applies updated exported fields to the in-memory user record if the user is currently online. Connection state and other runtime-only fields are preserved from the live record.

func ValidateName

func ValidateName(name string) error

func ValidatePassword

func ValidatePassword(pw string) error

Types

type ActiveUsers

type ActiveUsers struct {
	Users               map[int]*UserRecord                 // userId to UserRecord
	Usernames           map[string]int                      // username to userId
	Connections         map[connections.ConnectionId]int    // connectionId to userId
	UserConnections     map[int]connections.ConnectionId    // userId to connectionId
	LinkDeadConnections map[connections.ConnectionId]uint64 // connectionId to turn they became link-dead
}

type CharacterIndex added in v0.9.8

type CharacterIndex struct {
	// contains filtered or unexported fields
}

CharacterIndex is an in-memory index mapping lowercase character names to user IDs. One user ID may own multiple character names (active character plus any stored alts). The index is rebuilt at startup from user records; the alt-characters module is responsible for populating alt names.

func GetCharacterIndex added in v0.9.8

func GetCharacterIndex() *CharacterIndex

GetCharacterIndex returns the singleton CharacterIndex.

func (*CharacterIndex) Add added in v0.9.8

func (ci *CharacterIndex) Add(name string, userId int)

Add registers a character name as belonging to userId. The name is normalized to lowercase before storage. If the name is already present it is overwritten with the new userId.

func (*CharacterIndex) Find added in v0.9.8

func (ci *CharacterIndex) Find(name string) (userId int, found bool)

Find looks up a character name and returns the owning userId. The lookup is case-insensitive. Returns (0, false) when the name is not found.

func (*CharacterIndex) ForEach added in v0.9.8

func (ci *CharacterIndex) ForEach(fn func(name string, userId int) bool)

ForEach calls fn for every entry in the index in unspecified order. Returning false from fn stops iteration. The index is read-locked for the duration of the call; fn must not call any CharacterIndex method.

func (*CharacterIndex) Len added in v0.9.8

func (ci *CharacterIndex) Len() int

Len returns the number of character names currently in the index.

func (*CharacterIndex) Rebuild added in v0.9.8

func (ci *CharacterIndex) Rebuild()

Rebuild clears the index and repopulates it from every user record on disk plus all currently online users. Only active character names are added here; the alt-characters module is responsible for adding alt names after this runs.

func (*CharacterIndex) Remove added in v0.9.8

func (ci *CharacterIndex) Remove(name string)

Remove deletes the entry for name from the index. It is a no-op if the name is not present.

type CharacterSearchResult added in v0.9.8

type CharacterSearchResult struct {
	UserId        int    `json:"user_id"`
	Username      string `json:"username"`
	CharacterName string `json:"character_name"`
}

CharacterSearchResult holds the data returned by character search endpoints.

func SearchCharacters added in v0.9.8

func SearchCharacters(searchName string) []CharacterSearchResult

SearchCharacters searches the character index for names matching searchName. An exact match (case-insensitive) returns a single result. When there is no exact match, all names that have searchName as a prefix are returned (up to 100). The results include the owning userId and username.

type FunctionExporter added in v0.9.8

type FunctionExporter interface {
	GetExportedFunction(funcName string) (any, bool)
}

FunctionExporter is satisfied by the plugin registry, allowing the users package to call exported plugin functions without importing internal/plugins.

type IndexMetaData

type IndexMetaData struct {
	MetaDataSize uint64 // size of the metadata header (in bytes)
	IndexVersion uint64
	RecordCount  uint64
	RecordSize   uint64
	Checksum     uint64 // FNV-64 fingerprint of user directory contents; 0 when IndexChecksumEnabled is false
}

IndexMetaData holds header info that helps in reading the file.

func (IndexMetaData) Format

func (m IndexMetaData) Format() ([]byte, error)

Format formats the metadata header as a fixed-width string. The header (without newline) is exactly 99 bytes.

type IndexUserRecord

type IndexUserRecord struct {
	UserID   int64
	Username [80]byte
}

IndexUserRecord represents one fixed-width record.

type OnlineInfo

type OnlineInfo struct {
	Username      string
	CharacterName string
	Level         int
	Alignment     string
	Profession    string
	OnlineTime    int64
	OnlineTimeStr string
	IsAFK         bool
	Role          string
	ConnType      string
}

type Storage

type Storage struct {
	Items []items.Item
}

func (*Storage) AddItem

func (s *Storage) AddItem(i items.Item) bool

func (*Storage) FindItem

func (s *Storage) FindItem(itemName string) (items.Item, bool)

func (*Storage) GetItems

func (s *Storage) GetItems() []items.Item

func (*Storage) RemoveItem

func (s *Storage) RemoveItem(i items.Item) bool

type UserIndex

type UserIndex struct {
	Filename string
	// contains filtered or unexported fields
}

UserIndex is the central struct that holds the index filename and methods to work with the index.

func GetUserIndex added in v0.9.8

func GetUserIndex() *UserIndex

GetUserIndex returns the singleton UserIndex, initializing it if needed.

func InitUserIndex added in v0.9.8

func InitUserIndex() *UserIndex

InitUserIndex creates and initializes the singleton UserIndex. Called once at startup.

func (*UserIndex) AddUser

func (idx *UserIndex) AddUser(userId int, username string) error

AddUser appends a new record to the index file and updates the header.

func (*UserIndex) Create

func (idx *UserIndex) Create() error

Create initializes a new empty index file with a header.

func (*UserIndex) Delete

func (idx *UserIndex) Delete()

func (*UserIndex) Exists

func (idx *UserIndex) Exists() bool

func (*UserIndex) FindByUserId

func (idx *UserIndex) FindByUserId(userId int) (string, bool)

FindByUserId searches for a user record matching the provided userId.

func (*UserIndex) FindByUsername

func (idx *UserIndex) FindByUsername(username string) (int, bool)

FindByUsername searches the index for a username and returns its userId.

func (*UserIndex) ForEachRecord added in v0.9.8

func (idx *UserIndex) ForEachRecord(fn func(rec IndexUserRecord) bool)

ForEachRecord iterates over all index records, calling fn for each. Returning false from fn stops iteration.

func (*UserIndex) GetHighestUserId

func (idx *UserIndex) GetHighestUserId() int

func (*UserIndex) GetMetaData

func (idx *UserIndex) GetMetaData() IndexMetaData

func (*UserIndex) IsUpToDate added in v0.9.8

func (idx *UserIndex) IsUpToDate() bool

IsUpToDate returns true if the index file exists, has the current version, and its stored FNV-64 checksum matches the current state of the user directory.

func (*UserIndex) Rebuild

func (idx *UserIndex) Rebuild() error

Rebuild recreates the index from all offline user records. It calls Create() internally so it is self-contained. After building, it computes and persists a directory checksum so that IsUpToDate can detect stale indexes on the next startup.

func (*UserIndex) RemoveByUsername

func (idx *UserIndex) RemoveByUsername(username string) error

RemoveByUsername removes the first record matching the username and rewrites the index.

type UserLog

type UserLog []UserLogEntry

func (*UserLog) Add

func (ul *UserLog) Add(cat string, message string)

func (*UserLog) Items

func (ul *UserLog) Items(yield func(UserLogEntry) bool)

type UserLogEntry

type UserLogEntry struct {
	Category  string    // optional category such as "combat" "communication" etc
	WhenRound uint64    // Game round it occured
	WhenTime  time.Time // Actual time it occured
	What      string    // String describing occurance
}

type UserRecord

type UserRecord struct {
	UserId        int                   `yaml:"userid"`
	Role          string                `yaml:"role"`                  // user, mod, or admin
	Permissions   []string              `yaml:"permissions,omitempty"` // Discrete permissions for mod role
	Username      string                `yaml:"username"`
	Password      string                `yaml:"password"`
	Joined        time.Time             `yaml:"joined"`
	Macros        map[string]string     `yaml:"macros,omitempty"`  // Up to 10 macros, just string commands.
	Aliases       map[string]string     `yaml:"aliases,omitempty"` // string=>string remapping of commands
	Character     *characters.Character `yaml:"character,omitempty"`
	ConfigOptions map[string]any        `yaml:"configoptions,omitempty"`
	Muted         bool                  `yaml:"muted,omitempty"`        // Cannot SEND custom communications to anyone but admin/mods
	ScreenReader  bool                  `yaml:"screenreader,omitempty"` // Are they using a screen reader? (We should remove excess symbols)
	EmailAddress  string                `yaml:"emailaddress,omitempty"` // Email address (if provided)
	TipsComplete  map[string]bool       `yaml:"tipscomplete,omitempty"` // Tips the user has followed/completed so they can be quiet
	EventLog      UserLog               `yaml:"-"`                      // Do not retain in user file (for now)
	LastMusic     string                `yaml:"-"`                      // Keeps track of the last music that was played
	// contains filtered or unexported fields
}

func CopyoverReconnectUser added in v0.9.8

func CopyoverReconnectUser(user *UserRecord, connectionId connections.ConnectionId) (*UserRecord, string, error)

CopyoverReconnectUser takes over an existing session for a user who is reconnecting after a copyover. The caller must have already validated a one-time reconnect token that proves the identity of the connecting client. Unlike LoginUser, this always succeeds for users that are currently tracked (zombie or not), because the old connection is a stale copyover socket that is no longer connected.

func GetAllActiveUsers

func GetAllActiveUsers() []*UserRecord

func GetByCharacterName

func GetByCharacterName(name string) *UserRecord

func GetByConnectionId

func GetByConnectionId(connectionId connections.ConnectionId) *UserRecord

func GetByUserId

func GetByUserId(userId int) *UserRecord

func LoadUser

func LoadUser(username string, skipValidation ...bool) (*UserRecord, error)

func LoadUserById added in v0.9.8

func LoadUserById(userId int) (*UserRecord, error)

func LoginUser

func LoginUser(user *UserRecord, connectionId connections.ConnectionId) (*UserRecord, string, error)

First time creating a user.

func NewUserRecord

func NewUserRecord(userId int, connectionId uint64) *UserRecord

func (*UserRecord) AddBuff

func (u *UserRecord) AddBuff(buffId int, source string)

func (*UserRecord) AddCommandAlias

func (u *UserRecord) AddCommandAlias(input string, output string) (addedAlias string, deletedAlias string)

func (*UserRecord) BlockInput

func (u *UserRecord) BlockInput()

func (*UserRecord) ClearPrompt

func (u *UserRecord) ClearPrompt()

func (*UserRecord) ClientSettings

func (u *UserRecord) ClientSettings() connections.ClientSettings

func (*UserRecord) Command

func (u *UserRecord) Command(inputTxt string, waitSeconds ...float64)

func (*UserRecord) CommandFlagged

func (u *UserRecord) CommandFlagged(inputTxt string, flagData events.EventFlag, waitSeconds ...float64)

func (*UserRecord) ConnectionId

func (u *UserRecord) ConnectionId() uint64

func (*UserRecord) DidTip

func (u *UserRecord) DidTip(tipName string, completed ...bool) bool

func (*UserRecord) GetCommandPrompt

func (u *UserRecord) GetCommandPrompt() string

func (*UserRecord) GetConfigOption

func (u *UserRecord) GetConfigOption(key string) any

func (*UserRecord) GetConnectTime

func (u *UserRecord) GetConnectTime() time.Time

func (*UserRecord) GetLastInputRound

func (u *UserRecord) GetLastInputRound() uint64

func (*UserRecord) GetOnlineInfo

func (u *UserRecord) GetOnlineInfo() OnlineInfo

func (*UserRecord) GetPrompt

func (u *UserRecord) GetPrompt() *prompt.Prompt

func (*UserRecord) GetTempData

func (u *UserRecord) GetTempData(key string) any

func (*UserRecord) GetUnsentText

func (u *UserRecord) GetUnsentText() (unsent string, suggestion string)

func (*UserRecord) GrantXP

func (u *UserRecord) GrantXP(amt int, source string)

Grants experience to the user and notifies them Additionally accepts `source` as a short identifier of the XP source Example source: "combat", "quest progress", "trash cleanup", "exploration"

func (*UserRecord) HasPermission added in v0.9.8

func (u *UserRecord) HasPermission(permissionId string) bool

HasPermission is a convenience wrapper around HasRolePermission without the simpleMatch parameter — used by web middleware where prefix-only matching is always desired.

func (*UserRecord) HasPlaintextPassword added in v0.9.8

func (u *UserRecord) HasPlaintextPassword() bool

func (*UserRecord) HasRolePermission

func (u *UserRecord) HasRolePermission(permissionId string, simpleMatch ...bool) bool

HasRolePermission returns true when the user has the given permission. Admins always pass. Mods are checked against their Permissions slice using prefix-match semantics: a granted key of "room" also satisfies "room.edit", "room.edit.exits", etc. The optional simpleMatch parameter additionally allows the inverse direction (requested "room" matches granted "room.edit").

func (*UserRecord) HasShop

func (u *UserRecord) HasShop() bool

func (*UserRecord) InputBlocked

func (u *UserRecord) InputBlocked() bool

func (*UserRecord) PasswordMatches

func (u *UserRecord) PasswordMatches(input string) bool

func (*UserRecord) PlayMusic

func (u *UserRecord) PlayMusic(musicFileOrId string)

func (*UserRecord) PlaySound

func (u *UserRecord) PlaySound(soundId string, category string)

func (*UserRecord) ProcessPromptString

func (u *UserRecord) ProcessPromptString(promptStr string) string

func (*UserRecord) ReplaceCharacter

func (u *UserRecord) ReplaceCharacter(replacement *characters.Character)

Replace a characters information with another.

func (*UserRecord) RoundTick

func (u *UserRecord) RoundTick()

func (*UserRecord) SendText

func (u *UserRecord) SendText(txt string)

func (*UserRecord) SendWebClientCommand

func (u *UserRecord) SendWebClientCommand(txt string)

func (*UserRecord) SetCharacterName

func (u *UserRecord) SetCharacterName(cn string) error

func (*UserRecord) SetConfigOption

func (u *UserRecord) SetConfigOption(key string, value any)

func (*UserRecord) SetLastInputRound

func (u *UserRecord) SetLastInputRound(rdNum uint64)

func (*UserRecord) SetPassword

func (u *UserRecord) SetPassword(pw string) error

func (*UserRecord) SetTempData

func (u *UserRecord) SetTempData(key string, value any)

func (*UserRecord) SetUnsentText

func (u *UserRecord) SetUnsentText(t string, suggest string)

The purpose of SetUnsentText(), GetUnsentText() is to Capture what the user is typing so that when we redraw the "prompt" or status bar, we can redraw what they were in the middle of typing. I don't like the idea of capturing it every time they hit a key though There is probably a better way.

func (*UserRecord) SetUsername

func (u *UserRecord) SetUsername(un string) error

func (*UserRecord) ShorthandId

func (u *UserRecord) ShorthandId() string

func (*UserRecord) StartPrompt

func (u *UserRecord) StartPrompt(command string, rest string) (*prompt.Prompt, bool)

Prompt related functionality

func (*UserRecord) TempName

func (u *UserRecord) TempName() string

func (*UserRecord) TryCommandAlias

func (u *UserRecord) TryCommandAlias(input string) string

func (*UserRecord) UnblockInput

func (u *UserRecord) UnblockInput()

func (*UserRecord) WimpyCheck

func (u *UserRecord) WimpyCheck()

type UserSearchResult added in v0.9.8

type UserSearchResult struct {
	UserId   int    `json:"user_id"`
	Username string `json:"username"`
	Role     string `json:"role"`
	Email    string `json:"email"`
}

UserSearchResult holds the subset of user data returned by search endpoints.

func SearchUsers added in v0.9.8

func SearchUsers(searchName string) []UserSearchResult

SearchUsers searches for users matching searchName.

If searchName is a valid integer, the search matches by userId (exact match only). Otherwise it searches by username: an exact match returns a single result; all prefix matches are returned when there is no exact match. The username search is case-insensitive.

func SearchUsersByRole added in v0.9.8

func SearchUsersByRole(role string) []UserSearchResult

SearchUsersByRole returns all users whose Role matches the given role string (case-insensitive). Results are capped at 500.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL